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 */
74 call _KeGetCurrentIrql@0
78 /* We're not, bugcheck! */
83 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
88 /* Make sure that we are not attached and that APCs are not disabled */
89 movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
90 mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
102 push APC_INDEX_MISMATCH
103 call _KeBugCheckEx@20
107 /* Get the lowest stack limit and check if we can handle it */
108 lea eax, [esp-0x3000]
109 cmp eax, [ebx+KTHREAD_STACK_LIMIT]
112 /* We can't, we'll have to grow our stack */
114 call _MmGrowKernelStack@4
116 /* Quit if we failed */
120 /* Save the current callback stack */
122 push [ebx+KTHREAD_CALLBACK_STACK]
124 /* Get and save the trap frame */
125 mov edx, [ebx+KTHREAD_TRAP_FRAME]
128 /* Get and save the initial stack */
129 mov esi, [ebx+KTHREAD_INITIAL_STACK]
132 /* Set the new callback stack */
133 mov [ebx+KTHREAD_CALLBACK_STACK], esp
135 /* Align stack on 16-byte boundary */
139 /* Set destination and origin NPX Areas */
140 sub esp, NPX_FRAME_LENGTH
141 sub esi, NPX_FRAME_LENGTH
143 /* Disable interrupts so we can fill the NPX State */
146 /* Now copy the NPX State */
147 mov ecx, [esi+FN_CONTROL_WORD]
148 mov [esi+FN_CONTROL_WORD], ecx
149 mov ecx, [esi+FN_STATUS_WORD]
150 mov [esi+FN_STATUS_WORD], ecx
151 mov ecx, [esi+FN_TAG_WORD]
152 mov [esi+FN_TAG_WORD], ecx
153 mov ecx, [esi+FN_DATA_SELECTOR]
154 mov [esi+FN_DATA_SELECTOR], ecx
155 mov ecx, [esi+FN_CR0_NPX_STATE]
156 mov [esi+FN_CR0_NPX_STATE], ecx
159 mov esi, fs:[KPCR_TSS]
161 /* Set the stack address */
162 mov [ebx+KTHREAD_INITIAL_STACK], edi
164 /* Bias the stack for V86 mode */
167 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
172 /* Set new stack address in TSS */
173 mov [esi+KTSS_ESP0], ecx
175 /* Allocate the trap frame and set it */
176 sub esp, KTRAP_FRAME_V86_ES
179 /* Set copy iterator and dest/origin parameters and do the copy */
180 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
181 lea edi, [esp+KTRAP_FRAME_FS]
182 lea esi, [esp+KTRAP_FRAME_FS]
185 /* FIXME: Copy debug registers if needed */
187 /* Get user-mode dispatcher address and set it as EIP */
188 mov eax, _KeUserCallbackDispatcher
189 mov [esp+KTRAP_FRAME_EIP], eax
191 /* Set the exception list */
192 mov eax, [KPCR_EXCEPTION_LIST]
193 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
195 /* Set the previous mode */
196 mov eax, [EDX+KTRAP_FRAME_PREVIOUS_MODE]
197 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
199 /* Bring interrupts back */
202 /* Write the debug data */
203 mov edi, [ebp+KTRAP_FRAME_EBP]
204 mov edx, [ebp+KTRAP_FRAME_EIP]
205 mov [ebp+KTRAP_FRAME_DEBUGPOINTER], edx
206 mov dword ptr [ebp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
207 mov [ebp+KTRAP_FRAME_DEBUGEBP], ebx
208 mov [ebp+KTRAP_FRAME_DEBUGEIP], edi
210 /* Exit to user-mode */
214 /* Restore registers */
226 * @name NtCallbackReturn
228 * The NtCallbackReturn routine returns to kernel mode after a user-mode
229 * callback was done through KeUserModeCallback. It uses the callback frame
230 * which was setup in order to return the information, restores the stack,
231 * and resumes execution where it was left off.
234 * Pointer to a caller-allocated buffer where the return data
235 * from the user-mode function is located.
237 * @param ResultLength
238 * Size of the Output Buffer described above.
240 * @param CallbackStatus
241 * Status code of the callback operation.
243 * @return Status code of the callback operation.
245 * @remark This call MUST be paired with KeUserModeCallback.
248 .globl _NtCallbackReturn2@12
249 .func NtCallbackReturn2@12
250 _NtCallbackReturn2@12:
252 /* Get the current thread and make sure we have a callback stack */
253 mov eax, fs:[KPCR_CURRENT_THREAD]
254 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
258 /* Get the trap frame */
259 mov ebx, [eax+KTHREAD_TRAP_FRAME]
261 /* Restore the exception list */
262 mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
263 mov fs:[KPCR_EXCEPTION_LIST], edx
265 /* Get the result, the result length and the status */
270 /* Store the results in the callback stack */
271 mov ebx, [ecx+CBSTACK_RESULT]
273 mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
276 /* Get the previous stack */
279 /* Disable interrupts for NPX save and stack switch */
282 /* Get the initial stack and restore it */
283 mov esi, fs:[KPCR_INITIAL_STACK]
284 mov [eax+KTHREAD_INITIAL_STACK], ebx
286 /* Set desination and origin NPX Frames */
287 sub esi, NPX_FRAME_LENGTH
288 sub ebx, NPX_FRAME_LENGTH
291 mov edx, [esi+FN_CONTROL_WORD]
292 mov [ebx+FN_CONTROL_WORD], edx
293 mov edx, [esi+FN_STATUS_WORD]
294 mov [ebx+FN_STATUS_WORD], edx
295 mov edx, [esi+FN_TAG_WORD]
296 mov [ebx+FN_TAG_WORD], edx
297 mov edx, [esi+FN_DATA_SELECTOR]
298 mov [ebx+FN_DATA_SELECTOR], edx
299 mov edx, [esi+FN_CR0_NPX_STATE]
300 mov [ebx+FN_CR0_NPX_STATE], edx
302 /* Get saved trap frame and clear DR7 */
303 mov edi, [ecx+CBSTACK_TRAP_FRAME]
304 and dword ptr [edi+KTRAP_FRAME_DR7], 0
306 /* FIXME: Restore debug regs */
309 mov edx, fs:[KPCR_TSS]
311 /* Restore stack pointer */
312 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
314 /* Check if we were in V86 mode */
315 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
320 /* Restore the ESP in TSS */
321 mov [edx+KTSS_ESP0], ebx
323 /* Restore the trap frame */
324 mov [eax+KTHREAD_TRAP_FRAME], edi
326 /* Bring interrupts back */
329 /* Restore the callback stack*/
330 pop [eax+KTHREAD_CALLBACK_STACK]
332 /* Set status and return */
340 /* Clean stack and jump back */
347 mov eax, STATUS_NO_CALLBACK_ACTIVE
352 .globl _KeSwitchKernelStack@8
353 .func KeSwitchKernelStack@8
354 _KeSwitchKernelStack@8:
360 /* Get current thread */
361 mov edx, fs:[KPCR_CURRENT_THREAD]
363 /* Get new and current base */
365 mov ecx, [edx+KTHREAD_STACK_BASE]
367 /* Fixup the frame pointer */
371 /* Fixup the trap frame */
372 mov eax, [edx+KTHREAD_TRAP_FRAME]
375 mov [edx+KTHREAD_TRAP_FRAME], eax
377 /* Calculate stack size */
380 /* Get desination and origin */
384 /* Save stack pointer */
390 /* Restore stack pointer */
393 /* Save old stack base and get new limit/base */
394 mov eax, [edx+KTHREAD_STACK_BASE]
398 /* Disable interrupts for stack switch */
401 /* Set new base/limit */
402 mov [edx+KTHREAD_STACK_BASE], ecx
403 mov [edx+KTHREAD_STACK_LIMIT], esi
406 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
408 /* Set new initial stack */
409 mov [edx+KTHREAD_INITIAL_STACK], ecx
412 mov esi, [edx+KTHREAD_TRAP_FRAME]
415 mov edx, fs:[KPCR_TSS]
417 /* Check if we came from V86 mode */
418 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
420 /* Bias for NPX Area */
421 lea ecx, [ecx-NPX_FRAME_LENGTH]
427 /* Update ESP in TSS */
428 mov [edx+KTSS_ESP0], ecx
430 /* Update stack pointer */
433 /* Bring back interrupts and return */