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 ******************************************************************/
13 #include <internal/i386/asmmacro.S>
15 EXTERN _MmGrowKernelStack@4:PROC
16 EXTERN _KeUserCallbackDispatcher:PROC
17 EXTERN @KiServiceExit@8:PROC
19 /* FUNCTIONS ****************************************************************/
22 PUBLIC _KiGetUserModeStackAddress@0
23 _KiGetUserModeStackAddress@0:
25 /* Get the current thread's trapframe and return the esp */
26 mov eax, fs:[KPCR_CURRENT_THREAD]
27 mov eax, [eax+KTHREAD_TRAP_FRAME]
28 lea eax, [eax+KTRAP_FRAME_ESP]
33 * @name KiCallUserMode
35 * The KiSwitchToUserMode routine sets up a Trap Frame and a Callback stack
36 * for the purpose of switching to user mode. The actual final jump is done
37 * by KiServiceExit which will treat this as a syscall return.
40 * Pointer to a caller-allocated buffer where to receive the return data
41 * from the user-mode function
44 * Size of the Output Buffer described above.
46 * @return None. Jumps into KiServiceExit.
48 * @remark If there is not enough Kernel Stack space, the routine will increase the
51 * User mode execution resumes at ntdll!KiUserCallbackDispatcher.
53 * This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
56 PUBLIC _KiCallUserMode@8
59 /* Save volatile registers */
65 /* Get the current thread */
66 mov ebx, fs:[KPCR_CURRENT_THREAD]
68 /* Make sure we're at passive */
70 call _KeGetCurrentIrql@0
74 /* We're not, bugcheck! */
79 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
84 /* Make sure that we are not attached and that APCs are not disabled */
85 movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
86 mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
98 push APC_INDEX_MISMATCH
103 /* Get the lowest stack limit and check if we can handle it */
104 lea eax, [esp-HEX(3000)]
105 cmp eax, [ebx+KTHREAD_STACK_LIMIT]
108 /* We can't, we'll have to grow our stack */
110 call _MmGrowKernelStack@4
112 /* Quit if we failed */
116 /* Save the current callback stack */
118 push [ebx+KTHREAD_CALLBACK_STACK]
120 /* Get and save the trap frame */
121 mov edx, [ebx+KTHREAD_TRAP_FRAME]
124 /* Get and save the initial stack */
125 mov esi, [ebx+KTHREAD_INITIAL_STACK]
128 /* Set the new callback stack */
129 mov [ebx+KTHREAD_CALLBACK_STACK], esp
131 /* Align stack on 16-byte boundary */
135 /* Set destination and origin NPX Areas */
136 sub esp, NPX_FRAME_LENGTH
137 sub esi, NPX_FRAME_LENGTH
139 /* Disable interrupts so we can fill the NPX State */
142 /* Now copy the NPX State */
143 mov ecx, [esi+FP_CONTROL_WORD]
144 mov [esp+FP_CONTROL_WORD], ecx
145 mov ecx, [esi+FP_STATUS_WORD]
146 mov [esp+FP_STATUS_WORD], ecx
147 mov ecx, [esi+FP_TAG_WORD]
148 mov [esp+FP_TAG_WORD], ecx
149 mov ecx, [esi+FP_DATA_SELECTOR]
150 mov [esp+FP_DATA_SELECTOR], ecx
151 mov ecx, [esi+FN_CR0_NPX_STATE]
152 mov [esp+FN_CR0_NPX_STATE], ecx
155 mov esi, fs:[KPCR_TSS]
157 /* Set the stack address */
158 mov [ebx+KTHREAD_INITIAL_STACK], edi
160 /* Bias the stack for V86 mode */
163 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
168 /* Set new stack address in TSS */
169 mov [esi+KTSS_ESP0], ecx
171 /* Allocate the trap frame and set it */
172 sub esp, KTRAP_FRAME_V86_ES
175 /* Set copy iterator and dest/origin parameters and do the copy */
176 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
177 lea edi, [esp+KTRAP_FRAME_FS]
178 lea esi, [edx+KTRAP_FRAME_FS]
182 mov edi, [edx+KTRAP_FRAME_DR7]
183 test edi, NOT DR7_RESERVED_MASK
184 mov [esp+KTRAP_FRAME_DR7], edi
186 /* Check if we need to save debug registers */
189 /* Get user-mode dispatcher address and set it as EIP */
191 mov eax, _KeUserCallbackDispatcher
192 mov [esp+KTRAP_FRAME_EIP], eax
194 /* Set the exception list */
195 mov eax, fs:[KPCR_EXCEPTION_LIST]
196 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
198 /* Set the previous mode */
199 mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
200 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
202 /* Bring interrupts back */
205 /* Exit to user-mode */
213 lea edi, [esp+KTRAP_FRAME_DR0]
214 lea esi, [edx+KTRAP_FRAME_DR0]
219 /* Restore registers */
230 * @name NtCallbackReturn
232 * The NtCallbackReturn routine returns to kernel mode after a user-mode
233 * callback was done through KeUserModeCallback. It uses the callback frame
234 * which was setup in order to return the information, restores the stack,
235 * and resumes execution where it was left off.
238 * Pointer to a caller-allocated buffer where the return data
239 * from the user-mode function is located.
241 * @param ResultLength
242 * Size of the Output Buffer described above.
244 * @param CallbackStatus
245 * Status code of the callback operation.
247 * @return Status code of the callback operation.
249 * @remark This call MUST be paired with KeUserModeCallback.
252 PUBLIC _NtCallbackReturn@12
253 _NtCallbackReturn@12:
255 /* Get the current thread and make sure we have a callback stack */
256 mov eax, fs:[KPCR_CURRENT_THREAD]
257 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
261 /* Get the trap frame */
262 mov ebx, [eax+KTHREAD_TRAP_FRAME]
264 /* Restore the exception list */
265 mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
266 mov fs:[KPCR_EXCEPTION_LIST], edx
268 /* Get the result, the result length and the status */
273 /* Store the results in the callback stack */
274 mov ebx, [ecx+CBSTACK_RESULT]
276 mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
279 /* Get the previous stack */
282 /* Disable interrupts for NPX save and stack switch */
285 /* Get the initial stack and restore it */
286 mov esi, [eax+KTHREAD_INITIAL_STACK]
287 mov [eax+KTHREAD_INITIAL_STACK], ebx
289 /* Set desination and origin NPX Frames */
290 sub esi, NPX_FRAME_LENGTH
291 sub ebx, NPX_FRAME_LENGTH
294 mov edx, [esi+FP_CONTROL_WORD]
295 mov [ebx+FP_CONTROL_WORD], edx
296 mov edx, [esi+FP_STATUS_WORD]
297 mov [ebx+FP_STATUS_WORD], edx
298 mov edx, [esi+FP_TAG_WORD]
299 mov [ebx+FP_TAG_WORD], edx
300 mov edx, [esi+FP_DATA_SELECTOR]
301 mov [ebx+FP_DATA_SELECTOR], edx
302 mov edx, [esi+FN_CR0_NPX_STATE]
303 mov [ebx+FN_CR0_NPX_STATE], edx
305 /* Check if we failed in user mode */
306 cmp ebp, STATUS_CALLBACK_POP_STACK
307 mov edi, [ecx+CBSTACK_TRAP_FRAME]
313 and dword ptr [edi+KTRAP_FRAME_DR7], 0
315 /* Check if debugging was active */
316 test byte ptr [eax+KTHREAD_DEBUG_ACTIVE], HEX(0FF)
322 mov edx, fs:[KPCR_TSS]
324 /* Restore stack pointer */
325 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
327 /* Check if we were in V86 mode */
328 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
333 /* Restore the ESP in TSS */
334 mov [edx+KTSS_ESP0], ebx
336 /* Restore the trap frame */
337 mov [eax+KTHREAD_TRAP_FRAME], edi
339 /* Bring interrupts back */
342 /* Restore the callback stack*/
343 pop [eax+KTHREAD_CALLBACK_STACK]
345 /* Set status and return */
353 /* Clean stack and jump back */
358 /* Set size to copy */
359 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
361 /* Check if this was V86 mode */
362 mov esi, [eax+KTHREAD_TRAP_FRAME]
363 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
365 /* Save EDI and load destination */
367 lea edi, [edi+KTRAP_FRAME_FS]
372 /* Set source and copy */
373 lea esi, [esi+KTRAP_FRAME_FS]
376 /* Restore ECX and ECX */
377 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
382 /* Get a pointer to thread's trap frame */
383 mov esi, [eax+KTHREAD_TRAP_FRAME]
385 /* Copy debug registers data from it */
386 mov edx, [esi+KTRAP_FRAME_DR0]
387 mov [edi+KTRAP_FRAME_DR0], edx
388 mov edx, [esi+KTRAP_FRAME_DR1]
389 mov [edi+KTRAP_FRAME_DR1], edx
390 mov edx, [esi+KTRAP_FRAME_DR2]
391 mov [edi+KTRAP_FRAME_DR2], edx
392 mov edx, [esi+KTRAP_FRAME_DR3]
393 mov [edi+KTRAP_FRAME_DR3], edx
394 mov edx, [esi+KTRAP_FRAME_DR6]
395 mov [edi+KTRAP_FRAME_DR6], edx
396 mov edx, [esi+KTRAP_FRAME_DR7]
397 mov [edi+KTRAP_FRAME_DR7], edx
405 mov eax, STATUS_NO_CALLBACK_ACTIVE
409 * @name KeSwitchKernelStack
411 * The KeSwitchKernelStack routine switches from the current thread's stack
412 * to the new specified base and limit.
415 * Pointer to the new Stack Base of the thread.
418 * Pointer to the new Stack Limit of the thread.
420 * @return The previous Stack Base of the thread.
422 * @remark This routine should typically only be used when converting from a
423 * non-GUI to a GUI Thread. The caller is responsible for freeing the
424 * previous stack. The new stack values MUST be valid before calling
428 PUBLIC _KeSwitchKernelStack@8
429 _KeSwitchKernelStack@8:
435 /* Get current thread */
436 mov edx, fs:[KPCR_CURRENT_THREAD]
438 /* Get new and current base */
440 mov ecx, [edx+KTHREAD_STACK_BASE]
442 /* Fixup the frame pointer */
446 /* Fixup the trap frame */
447 mov eax, [edx+KTHREAD_TRAP_FRAME]
450 mov [edx+KTHREAD_TRAP_FRAME], eax
452 /* Calculate stack size */
455 /* Get desination and origin */
459 /* Save stack pointer */
465 /* Restore stack pointer */
468 /* Save old stack base and get new limit/base */
469 mov eax, [edx+KTHREAD_STACK_BASE]
473 /* Disable interrupts for stack switch */
476 /* Set new base/limit */
477 mov [edx+KTHREAD_STACK_BASE], ecx
478 mov [edx+KTHREAD_STACK_LIMIT], esi
481 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
483 /* Set new initial stack */
484 mov [edx+KTHREAD_INITIAL_STACK], ecx
487 mov esi, [edx+KTHREAD_TRAP_FRAME]
490 mov edx, fs:[KPCR_TSS]
492 /* Check if we came from V86 mode */
493 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
495 /* Bias for NPX Area */
496 lea ecx, [ecx-NPX_FRAME_LENGTH]
502 /* Update ESP in TSS */
503 mov [edx+KTSS_ESP0], ecx
505 /* Update stack pointer */
508 /* Bring back interrupts and return */