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 <bugcodes.h>
13 #define APC_INDEX_MISMATCH 1
14 #define IRQL_GT_ZERO_AT_SYSTEM_SERVICE 0x4A
15 #define STATUS_NO_CALLBACK_ACTIVE 0xC0000258
16 .intel_syntax noprefix
18 // This file is a work in progress. Most of the code is currently disabled.
20 /* GLOBALS ****************************************************************/
21 .extern PVOID _KeUserCallbackDispatcher
23 /* FUNCTIONS ****************************************************************/
25 .globl _KiGetUserModeStackAddress@0
26 .func KiGetUserModeStackAddress@0
27 _KiGetUserModeStackAddress@0:
29 /* Get the current thread's trapframe and return the esp */
30 mov eax, fs:[KPCR_CURRENT_THREAD]
31 mov eax, [eax+KTHREAD_TRAP_FRAME]
32 lea eax, [eax+KTRAP_FRAME_ESP]
37 * @name KiCallUserMode
39 * The KiSwitchToUserMode routine sets up a Trap Frame and a Callback stack
40 * for the purpose of switching to user mode. The actual final jump is done
41 * by KiServiceExit which will treat this as a syscall return.
44 * Pointer to a caller-allocated buffer where to receive the return data
45 * from the user-mode function
48 * Size of the Output Buffer described above.
50 * @return None. Jumps into KiServiceExit.
52 * @remark If there is not enough Kernel Stack space, the routine will increase the
55 * User mode execution resumes at ntdll!KiUserCallbackDispatcher.
57 * This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
60 .globl _KiCallUserMode@8
61 .func KiCallUserMode@8
64 /* Save volatile registers */
70 /* Get the current thread */
71 mov ebx, fs:[KPCR_CURRENT_THREAD]
73 /* Make sure we're at passive */
75 call _KeGetCurrentIrql@0
79 /* We're not, bugcheck! */
84 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
89 /* Make sure that we are not attached and that APCs are not disabled */
90 movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
91 mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
103 push APC_INDEX_MISMATCH
104 call _KeBugCheckEx@20
109 /* Get the lowest stack limit and check if we can handle it */
110 lea eax, [esp-0x3000]
111 cmp eax, [ebx+KTHREAD_STACK_LIMIT]
114 /* We can't, we'll have to grow our stack */
116 call _MmGrowKernelStack@4
118 /* Quit if we failed */
122 /* Save the current callback stack */
124 push [ebx+KTHREAD_CALLBACK_STACK]
126 /* Get and save the trap frame */
127 mov edx, [ebx+KTHREAD_TRAP_FRAME]
130 /* Get and save the initial stack */
131 mov esi, [ebx+KTHREAD_INITIAL_STACK]
134 /* Set the new callback stack */
135 mov [ebx+KTHREAD_CALLBACK_STACK], esp
137 /* Align stack on 16-byte boundary */
141 /* Set destination and origin NPX Areas */
142 sub esp, NPX_FRAME_LENGTH
143 sub esi, NPX_FRAME_LENGTH
145 /* Disable interrupts so we can fill the NPX State */
148 /* Now copy the NPX State */
149 mov ecx, [esi+FN_CONTROL_WORD]
150 mov [esp+FN_CONTROL_WORD], ecx
151 mov ecx, [esi+FN_STATUS_WORD]
152 mov [esp+FN_STATUS_WORD], ecx
153 mov ecx, [esi+FN_TAG_WORD]
154 mov [esp+FN_TAG_WORD], ecx
155 mov ecx, [esi+FN_DATA_SELECTOR]
156 mov [esp+FN_DATA_SELECTOR], ecx
157 mov ecx, [esi+FN_CR0_NPX_STATE]
158 mov [esp+FN_CR0_NPX_STATE], ecx
161 mov esi, fs:[KPCR_TSS]
163 /* Set the stack address */
164 mov [ebx+KTHREAD_INITIAL_STACK], edi
166 /* Bias the stack for V86 mode */
169 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
174 /* Set new stack address in TSS */
175 mov [esi+KTSS_ESP0], ecx
177 /* Allocate the trap frame and set it */
178 sub esp, KTRAP_FRAME_V86_ES
181 /* Set copy iterator and dest/origin parameters and do the copy */
182 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
183 lea edi, [esp+KTRAP_FRAME_FS]
184 lea esi, [edx+KTRAP_FRAME_FS]
187 /* FIXME: Copy debug registers if needed */
189 /* Get user-mode dispatcher address and set it as EIP */
190 mov eax, _KeUserCallbackDispatcher
191 mov [esp+KTRAP_FRAME_EIP], eax
193 /* Set the exception list */
194 mov eax, [KPCR_EXCEPTION_LIST]
195 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
197 /* Set the previous mode */
198 mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
199 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
201 /* Bring interrupts back */
204 /* Write the debug data */
205 mov edi, [ebp+KTRAP_FRAME_EBP]
206 mov edx, [ebp+KTRAP_FRAME_EIP]
207 mov [ebp+KTRAP_FRAME_DEBUGPOINTER], edx
208 mov dword ptr [ebp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
209 mov [ebp+KTRAP_FRAME_DEBUGEBP], ebx
210 mov [ebp+KTRAP_FRAME_DEBUGEIP], edi
212 /* Exit to user-mode */
216 /* Restore registers */
228 * @name NtCallbackReturn
230 * The NtCallbackReturn routine returns to kernel mode after a user-mode
231 * callback was done through KeUserModeCallback. It uses the callback frame
232 * which was setup in order to return the information, restores the stack,
233 * and resumes execution where it was left off.
236 * Pointer to a caller-allocated buffer where the return data
237 * from the user-mode function is located.
239 * @param ResultLength
240 * Size of the Output Buffer described above.
242 * @param CallbackStatus
243 * Status code of the callback operation.
245 * @return Status code of the callback operation.
247 * @remark This call MUST be paired with KeUserModeCallback.
250 .globl _NtCallbackReturn2@12
251 .func NtCallbackReturn2@12
252 _NtCallbackReturn2@12:
254 /* Get the current thread and make sure we have a callback stack */
255 call _KeBugCheckEx@20
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, fs:[KPCR_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+FN_CONTROL_WORD]
295 mov [ebx+FN_CONTROL_WORD], edx
296 mov edx, [esi+FN_STATUS_WORD]
297 mov [ebx+FN_STATUS_WORD], edx
298 mov edx, [esi+FN_TAG_WORD]
299 mov [ebx+FN_TAG_WORD], edx
300 mov edx, [esi+FN_DATA_SELECTOR]
301 mov [ebx+FN_DATA_SELECTOR], edx
302 mov edx, [esi+FN_CR0_NPX_STATE]
303 mov [ebx+FN_CR0_NPX_STATE], edx
305 /* Get saved trap frame and clear DR7 */
306 mov edi, [ecx+CBSTACK_TRAP_FRAME]
307 and dword ptr [edi+KTRAP_FRAME_DR7], 0
309 /* FIXME: Restore debug regs */
312 mov edx, fs:[KPCR_TSS]
314 /* Restore stack pointer */
315 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
317 /* Check if we were in V86 mode */
318 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
323 /* Restore the ESP in TSS */
324 mov [edx+KTSS_ESP0], ebx
326 /* Restore the trap frame */
327 mov [eax+KTHREAD_TRAP_FRAME], edi
329 /* Bring interrupts back */
332 /* Restore the callback stack*/
333 pop [eax+KTHREAD_CALLBACK_STACK]
335 /* Set status and return */
343 /* Clean stack and jump back */
350 mov eax, STATUS_NO_CALLBACK_ACTIVE
355 .globl _KeSwitchKernelStack@8
356 .func KeSwitchKernelStack@8
357 _KeSwitchKernelStack@8:
363 /* Get current thread */
364 mov edx, fs:[KPCR_CURRENT_THREAD]
366 /* Get new and current base */
368 mov ecx, [edx+KTHREAD_STACK_BASE]
370 /* Fixup the frame pointer */
374 /* Fixup the trap frame */
375 mov eax, [edx+KTHREAD_TRAP_FRAME]
378 mov [edx+KTHREAD_TRAP_FRAME], eax
380 /* Calculate stack size */
383 /* Get desination and origin */
387 /* Save stack pointer */
393 /* Restore stack pointer */
396 /* Save old stack base and get new limit/base */
397 mov eax, [edx+KTHREAD_STACK_BASE]
401 /* Disable interrupts for stack switch */
404 /* Set new base/limit */
405 mov [edx+KTHREAD_STACK_BASE], ecx
406 mov [edx+KTHREAD_STACK_LIMIT], esi
409 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
411 /* Set new initial stack */
412 mov [edx+KTHREAD_INITIAL_STACK], ecx
415 mov esi, [edx+KTHREAD_TRAP_FRAME]
418 mov edx, fs:[KPCR_TSS]
420 /* Check if we came from V86 mode */
421 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
423 /* Bias for NPX Area */
424 lea ecx, [ecx-NPX_FRAME_LENGTH]
430 /* Update ESP in TSS */
431 mov [edx+KTSS_ESP0], ecx
433 /* Update stack pointer */
436 /* Bring back interrupts and return */