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
KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,IN PKTRAP_FRAME TrapFrame,IN PKNORMAL_ROUTINE NormalRoutine,IN PVOID NormalContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)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 = { 0 };
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
KeUserModeCallback(IN ULONG RoutineIndex,IN PVOID Argument,IN ULONG ArgumentLength,OUT PVOID * Result,OUT PULONG ResultLength)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 }
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
212 /*
213 * Stack layout for KiUserModeCallout:
214 * ----------------------------------
215 * KCALLOUT_FRAME.ResultLength <= 2nd Parameter to KiCallUserMode
216 * KCALLOUT_FRAME.Result <= 1st Parameter to KiCallUserMode
217 * KCALLOUT_FRAME.ReturnAddress <= Return address of KiCallUserMode
218 * KCALLOUT_FRAME.Ebp \
219 * KCALLOUT_FRAME.Ebx | = non-volatile registers, pushed
220 * KCALLOUT_FRAME.Esi | by KiCallUserMode
221 * KCALLOUT_FRAME.Edi /
222 * KCALLOUT_FRAME.CallbackStack
223 * KCALLOUT_FRAME.TrapFrame
224 * KCALLOUT_FRAME.InitialStack <= CalloutFrame points here
225 * ----------------------------------
226 * ~~ optional alignment ~~
227 * ----------------------------------
228 * FX_SAVE_AREA
229 * ----------------------------------
230 * KTRAP_FRAME
231 * ----------------------------------
232 * ~~ begin of stack frame for KiUserModeCallout ~~
233 *
234 */
235
236 NTSTATUS
237 FASTCALL
KiUserModeCallout(PKCALLOUT_FRAME CalloutFrame)238 KiUserModeCallout(PKCALLOUT_FRAME CalloutFrame)
239 {
240 PKTHREAD CurrentThread;
241 PKTRAP_FRAME TrapFrame, CallbackTrapFrame;
242 PFX_SAVE_AREA FxSaveArea, OldFxSaveArea;
243 PKPCR Pcr;
244 PKTSS Tss;
245 ULONG_PTR InitialStack;
246 NTSTATUS Status;
247
248 /* Get the current thread */
249 CurrentThread = KeGetCurrentThread();
250
251 #if DBG
252 /* Check if we are at pasive level */
253 if (KeGetCurrentIrql() != PASSIVE_LEVEL)
254 {
255 /* We're not, bugcheck */
256 KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE,
257 0,
258 KeGetCurrentIrql(),
259 0,
260 0);
261 }
262
263 /* Check if we are attached or APCs are disabled */
264 if ((CurrentThread->ApcStateIndex != OriginalApcEnvironment) ||
265 (CurrentThread->CombinedApcDisable > 0))
266 {
267 KeBugCheckEx(APC_INDEX_MISMATCH,
268 0,
269 CurrentThread->ApcStateIndex,
270 CurrentThread->CombinedApcDisable,
271 0);
272 }
273 #endif
274
275 /* Align stack on a 16-byte boundary */
276 InitialStack = ALIGN_DOWN_BY(CalloutFrame, 16);
277
278 /* Check if we have enough space on the stack */
279 if ((InitialStack - KERNEL_STACK_SIZE) < CurrentThread->StackLimit)
280 {
281 /* We don't, we'll have to grow our stack */
282 Status = MmGrowKernelStack((PVOID)InitialStack);
283
284 /* Quit if we failed */
285 if (!NT_SUCCESS(Status))
286 {
287 if (Status == STATUS_STACK_OVERFLOW)
288 {
289 DPRINT1("Thread wants too much stack\n");
290 }
291
292 return Status;
293 }
294 }
295
296 /* Save the current callback stack and initial stack */
297 CalloutFrame->CallbackStack = (ULONG_PTR)CurrentThread->CallbackStack;
298 CalloutFrame->InitialStack = (ULONG_PTR)CurrentThread->InitialStack;
299
300 /* Get and save the trap frame */
301 TrapFrame = CurrentThread->TrapFrame;
302 CalloutFrame->TrapFrame = (ULONG_PTR)TrapFrame;
303
304 /* Set the new callback stack */
305 CurrentThread->CallbackStack = CalloutFrame;
306
307 /* Set destination and origin NPX Areas */
308 OldFxSaveArea = (PVOID)(CalloutFrame->InitialStack - sizeof(FX_SAVE_AREA));
309 FxSaveArea = (PVOID)(InitialStack - sizeof(FX_SAVE_AREA));
310
311 /* Disable interrupts so we can fill the NPX State */
312 _disable();
313
314 /* Now copy the NPX State */
315 FxSaveArea->U.FnArea.ControlWord = OldFxSaveArea->U.FnArea.ControlWord;
316 FxSaveArea->U.FnArea.StatusWord = OldFxSaveArea->U.FnArea.StatusWord;
317 FxSaveArea->U.FnArea.TagWord = OldFxSaveArea->U.FnArea.TagWord;
318 FxSaveArea->U.FnArea.DataSelector = OldFxSaveArea->U.FnArea.DataSelector;
319 FxSaveArea->Cr0NpxState = OldFxSaveArea->Cr0NpxState;
320
321 /* Set the stack address */
322 CurrentThread->InitialStack = (PVOID)InitialStack;
323
324 /* Locate the trap frame on the callback stack */
325 CallbackTrapFrame = (PVOID)((ULONG_PTR)FxSaveArea - sizeof(KTRAP_FRAME));
326
327 /* Copy the trap frame to the new location */
328 *CallbackTrapFrame = *TrapFrame;
329
330 /* Get PCR */
331 Pcr = KeGetPcr();
332
333 /* Update the exception list */
334 CallbackTrapFrame->ExceptionList = Pcr->NtTib.ExceptionList;
335
336 /* Get TSS */
337 Tss = Pcr->TSS;
338
339 /* Check for V86 mode */
340 if (CallbackTrapFrame->EFlags & EFLAGS_V86_MASK)
341 {
342 /* Set new stack address in TSS (full trap frame) */
343 Tss->Esp0 = (ULONG_PTR)(CallbackTrapFrame + 1);
344 }
345 else
346 {
347 /* Set new stack address in TSS (non-V86 trap frame) */
348 Tss->Esp0 = (ULONG_PTR)&CallbackTrapFrame->V86Es;
349 }
350
351 /* Set user-mode dispatcher address as EIP */
352 CallbackTrapFrame->Eip = (ULONG_PTR)KeUserCallbackDispatcher;
353
354 /* Bring interrupts back */
355 _enable();
356
357 /* Exit to user-mode */
358 KiServiceExit(CallbackTrapFrame, 0);
359 }
360
361 /*++
362 * @name NtCallbackReturn
363 *
364 * The NtCallbackReturn routine returns to kernel mode after a user-mode
365 * callback was done through KeUserModeCallback. It uses the callback frame
366 * which was setup in order to return the information, restores the stack,
367 * and resumes execution where it was left off.
368 *
369 * @param Result
370 * Pointer to a caller-allocated buffer where the return data
371 * from the user-mode function is located.
372 *
373 * @param ResultLength
374 * Size of the Output Buffer described above.
375 *
376 * @param CallbackStatus
377 * Status code of the callback operation.
378 *
379 * @return Status code of the callback operation.
380 *
381 * @remark This call MUST be paired with KeUserModeCallback.
382 *
383 *--*/
384 NTSTATUS
385 NTAPI
NtCallbackReturn(_In_ PVOID Result,_In_ ULONG ResultLength,_In_ NTSTATUS CallbackStatus)386 NtCallbackReturn(
387 _In_ PVOID Result,
388 _In_ ULONG ResultLength,
389 _In_ NTSTATUS CallbackStatus)
390 {
391 PKTHREAD CurrentThread;
392 PKCALLOUT_FRAME CalloutFrame;
393 PKTRAP_FRAME CallbackTrapFrame, TrapFrame;
394 PFX_SAVE_AREA FxSaveArea, CbFxSaveArea;
395 ULONG Size;
396 PKPCR Pcr;
397 PKTSS Tss;
398
399 /* Get the current thread and make sure we have a callback stack */
400 CurrentThread = KeGetCurrentThread();
401 CalloutFrame = CurrentThread->CallbackStack;
402 if (CalloutFrame == NULL)
403 {
404 return STATUS_NO_CALLBACK_ACTIVE;
405 }
406
407 /* Get the trap frame */
408 CallbackTrapFrame = CurrentThread->TrapFrame;
409
410 /* Restore the exception list */
411 Pcr = KeGetPcr();
412 Pcr->NtTib.ExceptionList = CallbackTrapFrame->ExceptionList;
413
414 /* Store the results in the callback stack */
415 *((PVOID*)CalloutFrame->Result) = Result;
416 *((ULONG*)CalloutFrame->ResultLength) = ResultLength;
417
418 /* Disable interrupts for NPX save and stack switch */
419 _disable();
420
421 /* Set desination and origin NPX Frames */
422 CbFxSaveArea = (PVOID)((ULONG)CurrentThread->InitialStack - sizeof(FX_SAVE_AREA));
423 FxSaveArea = (PVOID)(CalloutFrame->InitialStack - sizeof(FX_SAVE_AREA));
424
425 /* Now copy back NPX State */
426 FxSaveArea->U.FnArea.ControlWord = CbFxSaveArea->U.FnArea.ControlWord;
427 FxSaveArea->U.FnArea.StatusWord = CbFxSaveArea->U.FnArea.StatusWord;
428 FxSaveArea->U.FnArea.TagWord = CbFxSaveArea->U.FnArea.TagWord;
429 FxSaveArea->U.FnArea.DataSelector = CbFxSaveArea->U.FnArea.DataSelector;
430 FxSaveArea->Cr0NpxState = CbFxSaveArea->Cr0NpxState;
431
432 /* Get the previous trap frame */
433 TrapFrame = (PKTRAP_FRAME)CalloutFrame->TrapFrame;
434
435 /* Check if we failed in user mode */
436 if (CallbackStatus == STATUS_CALLBACK_POP_STACK)
437 {
438 /* Check if we came from v86 mode */
439 if (CallbackTrapFrame->EFlags & EFLAGS_V86_MASK)
440 {
441 Size = sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, SegFs);
442 }
443 else
444 {
445 Size = FIELD_OFFSET(KTRAP_FRAME, V86Es) - FIELD_OFFSET(KTRAP_FRAME, SegFs);
446 }
447
448 /* Copy back part of the trap frame */
449 RtlCopyMemory(&TrapFrame->SegFs, &CallbackTrapFrame->SegFs, Size);
450 }
451
452 /* Clear DR7 */
453 TrapFrame->Dr7 = 0;
454
455 /* Check if debugging was active */
456 if (CurrentThread->Header.DebugActive & 0xFF)
457 {
458 /* Copy debug registers data from it */
459 TrapFrame->Dr0 = CallbackTrapFrame->Dr0;
460 TrapFrame->Dr1 = CallbackTrapFrame->Dr1;
461 TrapFrame->Dr2 = CallbackTrapFrame->Dr2;
462 TrapFrame->Dr3 = CallbackTrapFrame->Dr3;
463 TrapFrame->Dr6 = CallbackTrapFrame->Dr6;
464 TrapFrame->Dr7 = CallbackTrapFrame->Dr7;
465 }
466
467 /* Get TSS */
468 Tss = Pcr->TSS;
469
470 /* Check for V86 mode */
471 if (TrapFrame->EFlags & EFLAGS_V86_MASK)
472 {
473 /* Set new stack address in TSS (full trap frame) */
474 Tss->Esp0 = (ULONG_PTR)(TrapFrame + 1);
475 }
476 else
477 {
478 /* Set new stack address in TSS (non-V86 trap frame) */
479 Tss->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
480 }
481
482 /* Get the initial stack and restore it */
483 CurrentThread->InitialStack = (PVOID)CalloutFrame->InitialStack;
484
485 /* Restore the trap frame and the previous callback stack */
486 CurrentThread->TrapFrame = TrapFrame;
487 CurrentThread->CallbackStack = (PVOID)CalloutFrame->CallbackStack;
488
489 /* Bring interrupts back */
490 _enable();
491
492 /* Now switch back to the old stack */
493 KiCallbackReturn(&CalloutFrame->Edi, CallbackStatus);
494 }
495
496
497 /* EOF */
498