xref: /reactos/ntoskrnl/ke/i386/usercall.c (revision 4d3df0da)
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         }
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
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
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