2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ke/i386/usercall_asm.S
5 * PURPOSE: User-Mode callbacks and return.
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES ******************************************************************/
12 #include <internal/i386/asmmacro.S>
13 .intel_syntax noprefix
15 /* FUNCTIONS ****************************************************************/
17 .globl _KiGetUserModeStackAddress@0
18 .func KiGetUserModeStackAddress@0
19 _KiGetUserModeStackAddress@0:
21 /* Get the current thread's trapframe and return the esp */
22 mov eax, fs:[KPCR_CURRENT_THREAD]
23 mov eax, [eax+KTHREAD_TRAP_FRAME]
24 lea eax, [eax+KTRAP_FRAME_ESP]
30 * @name KiCallUserMode
32 * The KiSwitchToUserMode routine sets up a Trap Frame and a Callback stack
33 * for the purpose of switching to user mode. The actual final jump is done
34 * by KiServiceExit which will treat this as a syscall return.
37 * Pointer to a caller-allocated buffer where to receive the return data
38 * from the user-mode function
41 * Size of the Output Buffer described above.
43 * @return None. Jumps into KiServiceExit.
45 * @remark If there is not enough Kernel Stack space, the routine will increase the
48 * User mode execution resumes at ntdll!KiUserCallbackDispatcher.
50 * This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
53 .globl _KiCallUserMode@8
54 .func KiCallUserMode@8
57 /* Save volatile registers */
63 /* Get the current thread */
64 mov ebx, fs:[KPCR_CURRENT_THREAD]
66 /* Make sure we're at passive */
68 call _KeGetCurrentIrql@0
72 /* We're not, bugcheck! */
77 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
82 /* Make sure that we are not attached and that APCs are not disabled */
83 movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
84 mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
96 push APC_INDEX_MISMATCH
101 /* Get the lowest stack limit and check if we can handle it */
102 lea eax, [esp-0x3000]
103 cmp eax, [ebx+KTHREAD_STACK_LIMIT]
106 /* We can't, we'll have to grow our stack */
108 call _MmGrowKernelStack@4
110 /* Quit if we failed */
114 /* Save the current callback stack */
116 push [ebx+KTHREAD_CALLBACK_STACK]
118 /* Get and save the trap frame */
119 mov edx, [ebx+KTHREAD_TRAP_FRAME]
122 /* Get and save the initial stack */
123 mov esi, [ebx+KTHREAD_INITIAL_STACK]
126 /* Set the new callback stack */
127 mov [ebx+KTHREAD_CALLBACK_STACK], esp
129 /* Align stack on 16-byte boundary */
133 /* Set destination and origin NPX Areas */
134 sub esp, NPX_FRAME_LENGTH
135 sub esi, NPX_FRAME_LENGTH
137 /* Disable interrupts so we can fill the NPX State */
140 /* Now copy the NPX State */
141 mov ecx, [esi+FP_CONTROL_WORD]
142 mov [esp+FP_CONTROL_WORD], ecx
143 mov ecx, [esi+FP_STATUS_WORD]
144 mov [esp+FP_STATUS_WORD], ecx
145 mov ecx, [esi+FP_TAG_WORD]
146 mov [esp+FP_TAG_WORD], ecx
147 mov ecx, [esi+FP_DATA_SELECTOR]
148 mov [esp+FP_DATA_SELECTOR], ecx
149 mov ecx, [esi+FN_CR0_NPX_STATE]
150 mov [esp+FN_CR0_NPX_STATE], ecx
153 mov esi, fs:[KPCR_TSS]
155 /* Set the stack address */
156 mov [ebx+KTHREAD_INITIAL_STACK], edi
158 /* Bias the stack for V86 mode */
161 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
166 /* Set new stack address in TSS */
167 mov [esi+KTSS_ESP0], ecx
169 /* Allocate the trap frame and set it */
170 sub esp, KTRAP_FRAME_V86_ES
173 /* Set copy iterator and dest/origin parameters and do the copy */
174 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
175 lea edi, [esp+KTRAP_FRAME_FS]
176 lea esi, [edx+KTRAP_FRAME_FS]
180 mov edi, [edx+KTRAP_FRAME_DR7]
181 test edi, ~DR7_RESERVED_MASK
182 mov [esp+KTRAP_FRAME_DR7], edi
184 /* Check if we need to save debug registers */
187 /* Get user-mode dispatcher address and set it as EIP */
189 mov eax, _KeUserCallbackDispatcher
190 mov [esp+KTRAP_FRAME_EIP], eax
192 /* Set the exception list */
193 mov eax, fs:[KPCR_EXCEPTION_LIST]
194 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
196 /* Set the previous mode */
197 mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
198 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
200 /* Bring interrupts back */
203 /* Write the debug data */
206 /* Exit to user-mode */
213 lea edi, [esp+KTRAP_FRAME_DR0]
214 lea esi, [edx+KTRAP_FRAME_DR0]
219 /* Restore registers */
231 * @name NtCallbackReturn
233 * The NtCallbackReturn routine returns to kernel mode after a user-mode
234 * callback was done through KeUserModeCallback. It uses the callback frame
235 * which was setup in order to return the information, restores the stack,
236 * and resumes execution where it was left off.
239 * Pointer to a caller-allocated buffer where the return data
240 * from the user-mode function is located.
242 * @param ResultLength
243 * Size of the Output Buffer described above.
245 * @param CallbackStatus
246 * Status code of the callback operation.
248 * @return Status code of the callback operation.
250 * @remark This call MUST be paired with KeUserModeCallback.
253 .globl _NtCallbackReturn@12
254 .func NtCallbackReturn@12
255 _NtCallbackReturn@12:
257 /* Get the current thread and make sure we have a callback stack */
258 mov eax, fs:[KPCR_CURRENT_THREAD]
259 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
263 /* Get the trap frame */
264 mov ebx, [eax+KTHREAD_TRAP_FRAME]
266 /* Restore the exception list */
267 mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
268 mov fs:[KPCR_EXCEPTION_LIST], edx
270 /* Get the result, the result length and the status */
275 /* Store the results in the callback stack */
276 mov ebx, [ecx+CBSTACK_RESULT]
278 mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
281 /* Get the previous stack */
284 /* Disable interrupts for NPX save and stack switch */
287 /* Get the initial stack and restore it */
288 mov esi, [eax+KTHREAD_INITIAL_STACK]
289 mov [eax+KTHREAD_INITIAL_STACK], ebx
291 /* Set desination and origin NPX Frames */
292 sub esi, NPX_FRAME_LENGTH
293 sub ebx, NPX_FRAME_LENGTH
296 mov edx, [esi+FP_CONTROL_WORD]
297 mov [ebx+FP_CONTROL_WORD], edx
298 mov edx, [esi+FP_STATUS_WORD]
299 mov [ebx+FP_STATUS_WORD], edx
300 mov edx, [esi+FP_TAG_WORD]
301 mov [ebx+FP_TAG_WORD], edx
302 mov edx, [esi+FP_DATA_SELECTOR]
303 mov [ebx+FP_DATA_SELECTOR], edx
304 mov edx, [esi+FN_CR0_NPX_STATE]
305 mov [ebx+FN_CR0_NPX_STATE], edx
307 /* Check if we failed in user mode */
308 cmp ebp, STATUS_CALLBACK_POP_STACK
309 mov edi, [ecx+CBSTACK_TRAP_FRAME]
315 and dword ptr [edi+KTRAP_FRAME_DR7], 0
317 /* Check if debugging was active */
318 test byte ptr [eax+KTHREAD_DEBUG_ACTIVE], 0xFF
324 mov edx, fs:[KPCR_TSS]
326 /* Restore stack pointer */
327 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
329 /* Check if we were in V86 mode */
330 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
335 /* Restore the ESP in TSS */
336 mov [edx+KTSS_ESP0], ebx
338 /* Restore the trap frame */
339 mov [eax+KTHREAD_TRAP_FRAME], edi
341 /* Bring interrupts back */
344 /* Restore the callback stack*/
345 pop [eax+KTHREAD_CALLBACK_STACK]
347 /* Set status and return */
355 /* Clean stack and jump back */
360 /* Set size to copy */
361 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
363 /* Check if this was V86 mode */
364 mov esi, [eax+KTHREAD_TRAP_FRAME]
365 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
367 /* Save EDI and load destination */
369 lea edi, [edi+KTRAP_FRAME_FS]
374 /* Set source and copy */
375 lea esi, [esi+KTRAP_FRAME_FS]
378 /* Restore ECX and ECX */
379 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
384 /* Get a pointer to thread's trap frame */
385 mov esi, [eax+KTHREAD_TRAP_FRAME]
387 /* Copy debug registers data from it */
388 mov edx, [esi+KTRAP_FRAME_DR0]
389 mov [edi+KTRAP_FRAME_DR0], edx
390 mov edx, [esi+KTRAP_FRAME_DR1]
391 mov [edi+KTRAP_FRAME_DR1], edx
392 mov edx, [esi+KTRAP_FRAME_DR2]
393 mov [edi+KTRAP_FRAME_DR2], edx
394 mov edx, [esi+KTRAP_FRAME_DR3]
395 mov [edi+KTRAP_FRAME_DR3], edx
396 mov edx, [esi+KTRAP_FRAME_DR6]
397 mov [edi+KTRAP_FRAME_DR6], edx
398 mov edx, [esi+KTRAP_FRAME_DR7]
399 mov [edi+KTRAP_FRAME_DR7], edx
407 mov eax, STATUS_NO_CALLBACK_ACTIVE
412 * @name KeSwitchKernelStack
414 * The KeSwitchKernelStack routine switches from the current thread's stack
415 * to the new specified base and limit.
418 * Pointer to the new Stack Base of the thread.
421 * Pointer to the new Stack Limit of the thread.
423 * @return The previous Stack Base of the thread.
425 * @remark This routine should typically only be used when converting from a
426 * non-GUI to a GUI Thread. The caller is responsible for freeing the
427 * previous stack. The new stack values MUST be valid before calling
431 .globl _KeSwitchKernelStack@8
432 .func KeSwitchKernelStack@8
433 _KeSwitchKernelStack@8:
439 /* Get current thread */
440 mov edx, fs:[KPCR_CURRENT_THREAD]
442 /* Get new and current base */
444 mov ecx, [edx+KTHREAD_STACK_BASE]
446 /* Fixup the frame pointer */
450 /* Fixup the trap frame */
451 mov eax, [edx+KTHREAD_TRAP_FRAME]
454 mov [edx+KTHREAD_TRAP_FRAME], eax
456 /* Calculate stack size */
459 /* Get desination and origin */
463 /* Save stack pointer */
469 /* Restore stack pointer */
472 /* Save old stack base and get new limit/base */
473 mov eax, [edx+KTHREAD_STACK_BASE]
477 /* Disable interrupts for stack switch */
480 /* Set new base/limit */
481 mov [edx+KTHREAD_STACK_BASE], ecx
482 mov [edx+KTHREAD_STACK_LIMIT], esi
485 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
487 /* Set new initial stack */
488 mov [edx+KTHREAD_INITIAL_STACK], ecx
491 mov esi, [edx+KTHREAD_TRAP_FRAME]
494 mov edx, fs:[KPCR_TSS]
496 /* Check if we came from V86 mode */
497 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
499 /* Bias for NPX Area */
500 lea ecx, [ecx-NPX_FRAME_LENGTH]
506 /* Update ESP in TSS */
507 mov [edx+KTSS_ESP0], ecx
509 /* Update stack pointer */
512 /* Bring back interrupts and return */