* Sync up to trunk head (r65426).
[reactos.git] / 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 * Timo Kreuzer (timo.kreuzer@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch;
17
18 /* PRIVATE FUNCTIONS *********************************************************/
19
20 /*++
21 * @name KiInitializeUserApc
22 *
23 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
24 *
25 * @param Reserved
26 * Pointer to the Exception Frame on non-i386 builds.
27 *
28 * @param TrapFrame
29 * Pointer to the Trap Frame.
30 *
31 * @param NormalRoutine
32 * Pointer to the NormalRoutine to call.
33 *
34 * @param NormalContext
35 * Pointer to the context to send to the Normal Routine.
36 *
37 * @param SystemArgument[1-2]
38 * Pointer to a set of two parameters that contain untyped data.
39 *
40 * @return None.
41 *
42 * @remarks None.
43 *
44 *--*/
45 VOID
46 NTAPI
47 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
48 IN PKTRAP_FRAME TrapFrame,
49 IN PKNORMAL_ROUTINE NormalRoutine,
50 IN PVOID NormalContext,
51 IN PVOID SystemArgument1,
52 IN PVOID SystemArgument2)
53 {
54 CONTEXT Context;
55 ULONG_PTR Stack, AlignedEsp;
56 ULONG ContextLength;
57 EXCEPTION_RECORD SehExceptRecord;
58
59 /* Don't deliver APCs in V86 mode */
60 if (TrapFrame->EFlags & EFLAGS_V86_MASK) return;
61
62 /* Save the full context */
63 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
64 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
65
66 /* Protect with SEH */
67 _SEH2_TRY
68 {
69 /* Sanity check */
70 ASSERT(KiUserTrap(TrapFrame));
71
72 /* Get the aligned size */
73 AlignedEsp = Context.Esp & ~3;
74 ContextLength = CONTEXT_ALIGNED_SIZE + (4 * sizeof(ULONG_PTR));
75 Stack = ((AlignedEsp - 8) & ~3) - ContextLength;
76
77 /* Probe the stack */
78 ProbeForWrite((PVOID)Stack, AlignedEsp - Stack, 1);
79 ASSERT(!(Stack & 3));
80
81 /* Copy data into it */
82 RtlCopyMemory((PVOID)(Stack + (4 * sizeof(ULONG_PTR))),
83 &Context,
84 sizeof(CONTEXT));
85
86 /* Run at APC dispatcher */
87 TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
88 TrapFrame->HardwareEsp = Stack;
89
90 /* Setup Ring 3 state */
91 TrapFrame->SegCs = Ke386SanitizeSeg(KGDT_R3_CODE, UserMode);
92 TrapFrame->HardwareSegSs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
93 TrapFrame->SegDs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
94 TrapFrame->SegEs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
95 TrapFrame->SegFs = Ke386SanitizeSeg(KGDT_R3_TEB, UserMode);
96 TrapFrame->SegGs = 0;
97 TrapFrame->ErrCode = 0;
98
99 /* Sanitize EFLAGS */
100 TrapFrame->EFlags = Ke386SanitizeFlags(Context.EFlags, UserMode);
101
102 /* Check if thread has IOPL and force it enabled if so */
103 if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
104
105 /* Setup the stack */
106 *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine;
107 *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext;
108 *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1;
109 *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2;
110 }
111 _SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER))
112 {
113 /* Dispatch the exception */
114 SehExceptRecord.ExceptionAddress = (PVOID)TrapFrame->Eip;
115 KiDispatchException(&SehExceptRecord,
116 ExceptionFrame,
117 TrapFrame,
118 UserMode,
119 TRUE);
120 }
121 _SEH2_END;
122 }
123
124 /* PUBLIC FUNCTIONS **********************************************************/
125
126 /*
127 * @implemented
128 */
129 NTSTATUS
130 NTAPI
131 KeUserModeCallback(IN ULONG RoutineIndex,
132 IN PVOID Argument,
133 IN ULONG ArgumentLength,
134 OUT PVOID *Result,
135 OUT PULONG ResultLength)
136 {
137 ULONG_PTR NewStack, OldStack;
138 PULONG UserEsp;
139 NTSTATUS CallbackStatus;
140 PEXCEPTION_REGISTRATION_RECORD ExceptionList;
141 PTEB Teb;
142 ULONG GdiBatchCount = 0;
143 ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
144 ASSERT(KeGetPreviousMode() == UserMode);
145
146 /* Get the current user-mode stack */
147 UserEsp = KiGetUserModeStackAddress();
148 OldStack = *UserEsp;
149
150 /* Enter a SEH Block */
151 _SEH2_TRY
152 {
153 /* Calculate and align the stack size */
154 NewStack = (OldStack - ArgumentLength) & ~3;
155
156 /* Make sure it's writable */
157 ProbeForWrite((PVOID)(NewStack - 6 * sizeof(ULONG_PTR)),
158 ArgumentLength + 6 * sizeof(ULONG_PTR),
159 sizeof(CHAR));
160
161 /* Copy the buffer into the stack */
162 RtlCopyMemory((PVOID)NewStack, Argument, ArgumentLength);
163
164 /* Write the arguments */
165 NewStack -= 24;
166 *(PULONG)NewStack = 0;
167 *(PULONG)(NewStack + 4) = RoutineIndex;
168 *(PULONG)(NewStack + 8) = (NewStack + 24);
169 *(PULONG)(NewStack + 12) = ArgumentLength;
170
171 /* Save the exception list */
172 Teb = KeGetCurrentThread()->Teb;
173 ExceptionList = Teb->NtTib.ExceptionList;
174
175 /* Jump to user mode */
176 *UserEsp = NewStack;
177 CallbackStatus = KiCallUserMode(Result, ResultLength);
178 if (CallbackStatus != STATUS_CALLBACK_POP_STACK)
179 {
180 /* Only restore the exception list if we didn't crash in ring 3 */
181 Teb->NtTib.ExceptionList = ExceptionList;
182 CallbackStatus = STATUS_SUCCESS;
183 }
184 else
185 {
186 /* Otherwise, pop the stack */
187 OldStack = *UserEsp;
188 }
189
190 /* Read the GDI Batch count */
191 GdiBatchCount = Teb->GdiBatchCount;
192 }
193 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
194 {
195 /* Get the SEH exception */
196 _SEH2_YIELD(return _SEH2_GetExceptionCode());
197 }
198 _SEH2_END;
199
200 /* Check if we have GDI Batch operations */
201 if (GdiBatchCount)
202 {
203 *UserEsp -= 256;
204 KeGdiFlushUserBatch();
205 }
206
207 /* Restore stack and return */
208 *UserEsp = OldStack;
209 return CallbackStatus;
210 }
211
212
213 /*
214 * Stack layout for KiUserModeCallout:
215 * ----------------------------------
216 * KCALLOUT_FRAME.ResultLength <= 2nd Parameter to KiCallUserMode
217 * KCALLOUT_FRAME.Result <= 1st Parameter to KiCallUserMode
218 * KCALLOUT_FRAME.ReturnAddress <= Return address of KiCallUserMode
219 * KCALLOUT_FRAME.Ebp \
220 * KCALLOUT_FRAME.Ebx | = non-volatile registers, pushed
221 * KCALLOUT_FRAME.Esi | by KiCallUserMode
222 * KCALLOUT_FRAME.Edi /
223 * KCALLOUT_FRAME.CallbackStack
224 * KCALLOUT_FRAME.TrapFrame
225 * KCALLOUT_FRAME.InitialStack <= CalloutFrame points here
226 * ----------------------------------
227 * ~~ optional alignment ~~
228 * ----------------------------------
229 * FX_SAVE_AREA
230 * ----------------------------------
231 * KTRAP_FRAME
232 * ----------------------------------
233 * ~~ begin of stack frame for KiUserModeCallout ~~
234 *
235 */
236
237 NTSTATUS
238 FASTCALL
239 KiUserModeCallout(PKCALLOUT_FRAME CalloutFrame)
240 {
241 PKTHREAD CurrentThread;
242 PKTRAP_FRAME TrapFrame, CallbackTrapFrame;
243 PFX_SAVE_AREA FxSaveArea, OldFxSaveArea;
244 PKPCR Pcr;
245 PKTSS Tss;
246 ULONG_PTR InitialStack;
247 NTSTATUS Status;
248
249 /* Get the current thread */
250 CurrentThread = KeGetCurrentThread();
251
252 #if DBG
253 /* Check if we are at pasive level */
254 if (KeGetCurrentIrql() != PASSIVE_LEVEL)
255 {
256 /* We're not, bugcheck */
257 KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE,
258 0,
259 KeGetCurrentIrql(),
260 0,
261 0);
262 }
263
264 /* Check if we are attached or APCs are disabled */
265 if ((CurrentThread->ApcStateIndex != OriginalApcEnvironment) ||
266 (CurrentThread->CombinedApcDisable > 0))
267 {
268 KeBugCheckEx(APC_INDEX_MISMATCH,
269 0,
270 CurrentThread->ApcStateIndex,
271 CurrentThread->CombinedApcDisable,
272 0);
273 }
274 #endif
275
276 /* Align stack on a 16-byte boundary */
277 InitialStack = ALIGN_DOWN_BY(CalloutFrame, 16);
278
279 /* Check if we have enough space on the stack */
280 if ((InitialStack - KERNEL_STACK_SIZE) < CurrentThread->StackLimit)
281 {
282 /* We don't, we'll have to grow our stack */
283 Status = MmGrowKernelStack((PVOID)InitialStack);
284
285 /* Quit if we failed */
286 if (!NT_SUCCESS(Status)) return Status;
287 }
288
289 /* Save the current callback stack and initial stack */
290 CalloutFrame->CallbackStack = (ULONG_PTR)CurrentThread->CallbackStack;
291 CalloutFrame->InitialStack = (ULONG_PTR)CurrentThread->InitialStack;
292
293 /* Get and save the trap frame */
294 TrapFrame = CurrentThread->TrapFrame;
295 CalloutFrame->TrapFrame = (ULONG_PTR)TrapFrame;
296
297 /* Set the new callback stack */
298 CurrentThread->CallbackStack = CalloutFrame;
299
300 /* Set destination and origin NPX Areas */
301 OldFxSaveArea = (PVOID)(CalloutFrame->InitialStack - sizeof(FX_SAVE_AREA));
302 FxSaveArea = (PVOID)(InitialStack - sizeof(FX_SAVE_AREA));
303
304 /* Disable interrupts so we can fill the NPX State */
305 _disable();
306
307 /* Now copy the NPX State */
308 FxSaveArea->U.FnArea.ControlWord = OldFxSaveArea->U.FnArea.ControlWord;
309 FxSaveArea->U.FnArea.StatusWord = OldFxSaveArea->U.FnArea.StatusWord;
310 FxSaveArea->U.FnArea.TagWord = OldFxSaveArea->U.FnArea.TagWord;
311 FxSaveArea->U.FnArea.DataSelector = OldFxSaveArea->U.FnArea.DataSelector;
312 FxSaveArea->Cr0NpxState = OldFxSaveArea->Cr0NpxState;
313
314 /* Set the stack address */
315 CurrentThread->InitialStack = (PVOID)InitialStack;
316
317 /* Locate the trap frame on the callback stack */
318 CallbackTrapFrame = (PVOID)((ULONG_PTR)FxSaveArea - sizeof(KTRAP_FRAME));
319
320 /* Copy the trap frame to the new location */
321 *CallbackTrapFrame = *TrapFrame;
322
323 /* Get PCR */
324 Pcr = KeGetPcr();
325
326 /* Update the exception list */
327 CallbackTrapFrame->ExceptionList = Pcr->NtTib.ExceptionList;
328
329 /* Get TSS */
330 Tss = Pcr->TSS;
331
332 /* Check for V86 mode */
333 if (CallbackTrapFrame->EFlags & EFLAGS_V86_MASK)
334 {
335 /* Set new stack address in TSS (full trap frame) */
336 Tss->Esp0 = (ULONG_PTR)(CallbackTrapFrame + 1);
337 }
338 else
339 {
340 /* Set new stack address in TSS (non-V86 trap frame) */
341 Tss->Esp0 = (ULONG_PTR)&CallbackTrapFrame->V86Es;
342 }
343
344 /* Set user-mode dispatcher address as EIP */
345 CallbackTrapFrame->Eip = (ULONG_PTR)KeUserCallbackDispatcher;
346
347 /* Bring interrupts back */
348 _enable();
349
350 /* Exit to user-mode */
351 KiServiceExit(CallbackTrapFrame, 0);
352 }
353
354 /*++
355 * @name NtCallbackReturn
356 *
357 * The NtCallbackReturn routine returns to kernel mode after a user-mode
358 * callback was done through KeUserModeCallback. It uses the callback frame
359 * which was setup in order to return the information, restores the stack,
360 * and resumes execution where it was left off.
361 *
362 * @param Result
363 * Pointer to a caller-allocated buffer where the return data
364 * from the user-mode function is located.
365 *
366 * @param ResultLength
367 * Size of the Output Buffer described above.
368 *
369 * @param CallbackStatus
370 * Status code of the callback operation.
371 *
372 * @return Status code of the callback operation.
373 *
374 * @remark This call MUST be paired with KeUserModeCallback.
375 *
376 *--*/
377 NTSTATUS
378 NTAPI
379 NtCallbackReturn(
380 _In_ PVOID Result,
381 _In_ ULONG ResultLength,
382 _In_ NTSTATUS CallbackStatus)
383 {
384 PKTHREAD CurrentThread;
385 PKCALLOUT_FRAME CalloutFrame;
386 PKTRAP_FRAME CallbackTrapFrame, TrapFrame;
387 PFX_SAVE_AREA FxSaveArea, CbFxSaveArea;
388 ULONG Size;
389 PKPCR Pcr;
390 PKTSS Tss;
391
392 /* Get the current thread and make sure we have a callback stack */
393 CurrentThread = KeGetCurrentThread();
394 CalloutFrame = CurrentThread->CallbackStack;
395 if (CalloutFrame == NULL)
396 {
397 return STATUS_NO_CALLBACK_ACTIVE;
398 }
399
400 /* Get the trap frame */
401 CallbackTrapFrame = CurrentThread->TrapFrame;
402
403 /* Restore the exception list */
404 Pcr = KeGetPcr();
405 Pcr->NtTib.ExceptionList = CallbackTrapFrame->ExceptionList;
406
407 /* Store the results in the callback stack */
408 *((PVOID*)CalloutFrame->Result) = Result;
409 *((ULONG*)CalloutFrame->ResultLength) = ResultLength;
410
411 /* Disable interrupts for NPX save and stack switch */
412 _disable();
413
414 /* Set desination and origin NPX Frames */
415 CbFxSaveArea = (PVOID)((ULONG)CurrentThread->InitialStack - sizeof(FX_SAVE_AREA));
416 FxSaveArea = (PVOID)(CalloutFrame->InitialStack - sizeof(FX_SAVE_AREA));
417
418 /* Now copy back NPX State */
419 FxSaveArea->U.FnArea.ControlWord = CbFxSaveArea->U.FnArea.ControlWord;
420 FxSaveArea->U.FnArea.StatusWord = CbFxSaveArea->U.FnArea.StatusWord;
421 FxSaveArea->U.FnArea.TagWord = CbFxSaveArea->U.FnArea.TagWord;
422 FxSaveArea->U.FnArea.DataSelector = CbFxSaveArea->U.FnArea.DataSelector;
423 FxSaveArea->Cr0NpxState = CbFxSaveArea->Cr0NpxState;
424
425 /* Get the previous trap frame */
426 TrapFrame = (PKTRAP_FRAME)CalloutFrame->TrapFrame;
427
428 /* Check if we failed in user mode */
429 if (CallbackStatus == STATUS_CALLBACK_POP_STACK)
430 {
431 /* Check if we came from v86 mode */
432 if (CallbackTrapFrame->EFlags & EFLAGS_V86_MASK)
433 {
434 Size = sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, SegFs);
435 }
436 else
437 {
438 Size = FIELD_OFFSET(KTRAP_FRAME, V86Es) - FIELD_OFFSET(KTRAP_FRAME, SegFs);
439 }
440
441 /* Copy back part of the trap frame */
442 RtlCopyMemory(&TrapFrame->SegFs, &CallbackTrapFrame->SegFs, Size);
443 }
444
445 /* Clear DR7 */
446 TrapFrame->Dr7 = 0;
447
448 /* Check if debugging was active */
449 if (CurrentThread->Header.DebugActive & 0xFF)
450 {
451 /* Copy debug registers data from it */
452 TrapFrame->Dr0 = CallbackTrapFrame->Dr0;
453 TrapFrame->Dr1 = CallbackTrapFrame->Dr1;
454 TrapFrame->Dr2 = CallbackTrapFrame->Dr2;
455 TrapFrame->Dr3 = CallbackTrapFrame->Dr3;
456 TrapFrame->Dr6 = CallbackTrapFrame->Dr6;
457 TrapFrame->Dr7 = CallbackTrapFrame->Dr7;
458 }
459
460 /* Get TSS */
461 Tss = Pcr->TSS;
462
463 /* Check for V86 mode */
464 if (TrapFrame->EFlags & EFLAGS_V86_MASK)
465 {
466 /* Set new stack address in TSS (full trap frame) */
467 Tss->Esp0 = (ULONG_PTR)(TrapFrame + 1);
468 }
469 else
470 {
471 /* Set new stack address in TSS (non-V86 trap frame) */
472 Tss->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
473 }
474
475 /* Get the initial stack and restore it */
476 CurrentThread->InitialStack = (PVOID)CalloutFrame->InitialStack;
477
478 /* Restore the trap frame and the previous callback stack */
479 CurrentThread->TrapFrame = TrapFrame;
480 CurrentThread->CallbackStack = (PVOID)CalloutFrame->CallbackStack;
481
482 /* Bring interrupts back */
483 _enable();
484
485 /* Now switch back to the old stack */
486 KiCallbackReturn(&CalloutFrame->Edi, CallbackStatus);
487 }
488
489
490 /* EOF */