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)
10 /* INCLUDES ******************************************************************/
16 extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch
;
18 /* PRIVATE FUNCTIONS *********************************************************/
21 * @name KiInitializeUserApc
23 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
26 * Pointer to the Exception Frame on non-i386 builds.
29 * Pointer to the Trap Frame.
31 * @param NormalRoutine
32 * Pointer to the NormalRoutine to call.
34 * @param NormalContext
35 * Pointer to the context to send to the Normal Routine.
37 * @param SystemArgument[1-2]
38 * Pointer to a set of two parameters that contain untyped data.
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
)
55 ULONG_PTR Stack
, AlignedEsp
;
57 EXCEPTION_RECORD SehExceptRecord
;
59 /* Don't deliver APCs in V86 mode */
60 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
) return;
62 /* Save the full context */
63 Context
.ContextFlags
= CONTEXT_FULL
| CONTEXT_DEBUG_REGISTERS
;
64 KeTrapFrameToContext(TrapFrame
, ExceptionFrame
, &Context
);
66 /* Protect with SEH */
70 ASSERT(KiUserTrap(TrapFrame
));
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
;
78 ProbeForWrite((PVOID
)Stack
, AlignedEsp
- Stack
, 1);
81 /* Copy data into it */
82 RtlCopyMemory((PVOID
)(Stack
+ (4 * sizeof(ULONG_PTR
))),
86 /* Run at APC dispatcher */
87 TrapFrame
->Eip
= (ULONG
)KeUserApcDispatcher
;
88 TrapFrame
->HardwareEsp
= Stack
;
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
);
97 TrapFrame
->ErrCode
= 0;
100 TrapFrame
->EFlags
= Ke386SanitizeFlags(Context
.EFlags
, UserMode
);
102 /* Check if thread has IOPL and force it enabled if so */
103 if (KeGetCurrentThread()->Iopl
) TrapFrame
->EFlags
|= EFLAGS_IOPL
;
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
;
111 _SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord
, _SEH2_GetExceptionInformation()->ExceptionRecord
, sizeof(EXCEPTION_RECORD
)), EXCEPTION_EXECUTE_HANDLER
))
113 /* Dispatch the exception */
114 SehExceptRecord
.ExceptionAddress
= (PVOID
)TrapFrame
->Eip
;
115 KiDispatchException(&SehExceptRecord
,
124 /* PUBLIC FUNCTIONS **********************************************************/
131 KeUserModeCallback(IN ULONG RoutineIndex
,
133 IN ULONG ArgumentLength
,
135 OUT PULONG ResultLength
)
137 ULONG_PTR NewStack
, OldStack
;
139 NTSTATUS CallbackStatus
;
140 PEXCEPTION_REGISTRATION_RECORD ExceptionList
;
142 ULONG GdiBatchCount
= 0;
143 ASSERT(KeGetCurrentThread()->ApcState
.KernelApcInProgress
== FALSE
);
144 ASSERT(KeGetPreviousMode() == UserMode
);
146 /* Get the current user-mode stack */
147 UserEsp
= KiGetUserModeStackAddress();
150 /* Enter a SEH Block */
153 /* Calculate and align the stack size */
154 NewStack
= (OldStack
- ArgumentLength
) & ~3;
156 /* Make sure it's writable */
157 ProbeForWrite((PVOID
)(NewStack
- 6 * sizeof(ULONG_PTR
)),
158 ArgumentLength
+ 6 * sizeof(ULONG_PTR
),
161 /* Copy the buffer into the stack */
162 RtlCopyMemory((PVOID
)NewStack
, Argument
, ArgumentLength
);
164 /* Write the arguments */
166 *(PULONG
)NewStack
= 0;
167 *(PULONG
)(NewStack
+ 4) = RoutineIndex
;
168 *(PULONG
)(NewStack
+ 8) = (NewStack
+ 24);
169 *(PULONG
)(NewStack
+ 12) = ArgumentLength
;
171 /* Save the exception list */
172 Teb
= KeGetCurrentThread()->Teb
;
173 ExceptionList
= Teb
->NtTib
.ExceptionList
;
175 /* Jump to user mode */
177 CallbackStatus
= KiCallUserMode(Result
, ResultLength
);
178 if (CallbackStatus
!= STATUS_CALLBACK_POP_STACK
)
180 /* Only restore the exception list if we didn't crash in ring 3 */
181 Teb
->NtTib
.ExceptionList
= ExceptionList
;
182 CallbackStatus
= STATUS_SUCCESS
;
186 /* Otherwise, pop the stack */
190 /* Read the GDI Batch count */
191 GdiBatchCount
= Teb
->GdiBatchCount
;
193 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
195 /* Get the SEH exception */
196 _SEH2_YIELD(return _SEH2_GetExceptionCode());
200 /* Check if we have GDI Batch operations */
204 KeGdiFlushUserBatch();
207 /* Restore stack and return */
209 return CallbackStatus
;
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 * ----------------------------------
230 * ----------------------------------
232 * ----------------------------------
233 * ~~ begin of stack frame for KiUserModeCallout ~~
239 KiUserModeCallout(PKCALLOUT_FRAME CalloutFrame
)
241 PKTHREAD CurrentThread
;
242 PKTRAP_FRAME TrapFrame
, CallbackTrapFrame
;
243 PFX_SAVE_AREA FxSaveArea
, OldFxSaveArea
;
246 ULONG_PTR InitialStack
;
249 /* Get the current thread */
250 CurrentThread
= KeGetCurrentThread();
253 /* Check if we are at pasive level */
254 if (KeGetCurrentIrql() != PASSIVE_LEVEL
)
256 /* We're not, bugcheck */
257 KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE
,
264 /* Check if we are attached or APCs are disabled */
265 if ((CurrentThread
->ApcStateIndex
!= OriginalApcEnvironment
) ||
266 (CurrentThread
->CombinedApcDisable
> 0))
268 KeBugCheckEx(APC_INDEX_MISMATCH
,
270 CurrentThread
->ApcStateIndex
,
271 CurrentThread
->CombinedApcDisable
,
276 /* Align stack on a 16-byte boundary */
277 InitialStack
= ALIGN_DOWN_BY(CalloutFrame
, 16);
279 /* Check if we have enough space on the stack */
280 if ((InitialStack
- KERNEL_STACK_SIZE
) < CurrentThread
->StackLimit
)
282 /* We don't, we'll have to grow our stack */
283 Status
= MmGrowKernelStack((PVOID
)InitialStack
);
285 /* Quit if we failed */
286 if (!NT_SUCCESS(Status
)) return Status
;
289 /* Save the current callback stack and initial stack */
290 CalloutFrame
->CallbackStack
= (ULONG_PTR
)CurrentThread
->CallbackStack
;
291 CalloutFrame
->InitialStack
= (ULONG_PTR
)CurrentThread
->InitialStack
;
293 /* Get and save the trap frame */
294 TrapFrame
= CurrentThread
->TrapFrame
;
295 CalloutFrame
->TrapFrame
= (ULONG_PTR
)TrapFrame
;
297 /* Set the new callback stack */
298 CurrentThread
->CallbackStack
= CalloutFrame
;
300 /* Set destination and origin NPX Areas */
301 OldFxSaveArea
= (PVOID
)(CalloutFrame
->InitialStack
- sizeof(FX_SAVE_AREA
));
302 FxSaveArea
= (PVOID
)(InitialStack
- sizeof(FX_SAVE_AREA
));
304 /* Disable interrupts so we can fill the NPX State */
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
;
314 /* Set the stack address */
315 CurrentThread
->InitialStack
= (PVOID
)InitialStack
;
317 /* Locate the trap frame on the callback stack */
318 CallbackTrapFrame
= (PVOID
)((ULONG_PTR
)FxSaveArea
- sizeof(KTRAP_FRAME
));
320 /* Copy the trap frame to the new location */
321 *CallbackTrapFrame
= *TrapFrame
;
326 /* Update the exception list */
327 CallbackTrapFrame
->ExceptionList
= Pcr
->NtTib
.ExceptionList
;
332 /* Check for V86 mode */
333 if (CallbackTrapFrame
->EFlags
& EFLAGS_V86_MASK
)
335 /* Set new stack address in TSS (full trap frame) */
336 Tss
->Esp0
= (ULONG_PTR
)(CallbackTrapFrame
+ 1);
340 /* Set new stack address in TSS (non-V86 trap frame) */
341 Tss
->Esp0
= (ULONG_PTR
)&CallbackTrapFrame
->V86Es
;
344 /* Set user-mode dispatcher address as EIP */
345 CallbackTrapFrame
->Eip
= (ULONG_PTR
)KeUserCallbackDispatcher
;
347 /* Bring interrupts back */
350 /* Exit to user-mode */
351 KiServiceExit(CallbackTrapFrame
, 0);
355 * @name NtCallbackReturn
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.
363 * Pointer to a caller-allocated buffer where the return data
364 * from the user-mode function is located.
366 * @param ResultLength
367 * Size of the Output Buffer described above.
369 * @param CallbackStatus
370 * Status code of the callback operation.
372 * @return Status code of the callback operation.
374 * @remark This call MUST be paired with KeUserModeCallback.
381 _In_ ULONG ResultLength
,
382 _In_ NTSTATUS CallbackStatus
)
384 PKTHREAD CurrentThread
;
385 PKCALLOUT_FRAME CalloutFrame
;
386 PKTRAP_FRAME CallbackTrapFrame
, TrapFrame
;
387 PFX_SAVE_AREA FxSaveArea
, CbFxSaveArea
;
392 /* Get the current thread and make sure we have a callback stack */
393 CurrentThread
= KeGetCurrentThread();
394 CalloutFrame
= CurrentThread
->CallbackStack
;
395 if (CalloutFrame
== NULL
)
397 return STATUS_NO_CALLBACK_ACTIVE
;
400 /* Get the trap frame */
401 CallbackTrapFrame
= CurrentThread
->TrapFrame
;
403 /* Restore the exception list */
405 Pcr
->NtTib
.ExceptionList
= CallbackTrapFrame
->ExceptionList
;
407 /* Store the results in the callback stack */
408 *((PVOID
*)CalloutFrame
->Result
) = Result
;
409 *((ULONG
*)CalloutFrame
->ResultLength
) = ResultLength
;
411 /* Disable interrupts for NPX save and stack switch */
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
));
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
;
425 /* Get the previous trap frame */
426 TrapFrame
= (PKTRAP_FRAME
)CalloutFrame
->TrapFrame
;
428 /* Check if we failed in user mode */
429 if (CallbackStatus
== STATUS_CALLBACK_POP_STACK
)
431 /* Check if we came from v86 mode */
432 if (CallbackTrapFrame
->EFlags
& EFLAGS_V86_MASK
)
434 Size
= sizeof(KTRAP_FRAME
) - FIELD_OFFSET(KTRAP_FRAME
, SegFs
);
438 Size
= FIELD_OFFSET(KTRAP_FRAME
, V86Es
) - FIELD_OFFSET(KTRAP_FRAME
, SegFs
);
441 /* Copy back part of the trap frame */
442 RtlCopyMemory(&TrapFrame
->SegFs
, &CallbackTrapFrame
->SegFs
, Size
);
448 /* Check if debugging was active */
449 if (CurrentThread
->Header
.DebugActive
& 0xFF)
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
;
463 /* Check for V86 mode */
464 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
)
466 /* Set new stack address in TSS (full trap frame) */
467 Tss
->Esp0
= (ULONG_PTR
)(TrapFrame
+ 1);
471 /* Set new stack address in TSS (non-V86 trap frame) */
472 Tss
->Esp0
= (ULONG_PTR
)&TrapFrame
->V86Es
;
475 /* Get the initial stack and restore it */
476 CurrentThread
->InitialStack
= (PVOID
)CalloutFrame
->InitialStack
;
478 /* Restore the trap frame and the previous callback stack */
479 CurrentThread
->TrapFrame
= TrapFrame
;
480 CurrentThread
->CallbackStack
= (PVOID
)CalloutFrame
->CallbackStack
;
482 /* Bring interrupts back */
485 /* Now switch back to the old stack */
486 KiCallbackReturn(&CalloutFrame
->Edi
, CallbackStatus
);