[CMAKE]
[reactos.git] / ntoskrnl / ke / i386 / usercall_asm.S
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <reactos/asm.h>
12 #include <ndk/i386/asm.h>
13 #include <internal/i386/asmmacro.S>
14
15 /* FUNCTIONS ****************************************************************/
16
17 .code32
18 .text
19
20 .globl _KiGetUserModeStackAddress@0
21 .func KiGetUserModeStackAddress@0
22 _KiGetUserModeStackAddress@0:
23
24 /* Get the current thread's trapframe and return the esp */
25 mov eax, fs:[KPCR_CURRENT_THREAD]
26 mov eax, [eax+KTHREAD_TRAP_FRAME]
27 lea eax, [eax+KTRAP_FRAME_ESP]
28 ret
29
30 .endfunc
31
32 /*++
33 * @name KiCallUserMode
34 *
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.
38 *
39 * @param OutputBuffer
40 * Pointer to a caller-allocated buffer where to receive the return data
41 * from the user-mode function
42 *
43 * @param OutputLength
44 * Size of the Output Buffer described above.
45 *
46 * @return None. Jumps into KiServiceExit.
47 *
48 * @remark If there is not enough Kernel Stack space, the routine will increase the
49 * Kernel Stack.
50 *
51 * User mode execution resumes at ntdll!KiUserCallbackDispatcher.
52 *
53 * This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
54 *
55 *--*/
56 .globl _KiCallUserMode@8
57 .func KiCallUserMode@8
58 _KiCallUserMode@8:
59
60 /* Save volatile registers */
61 push ebp
62 push ebx
63 push esi
64 push edi
65
66 /* Get the current thread */
67 mov ebx, fs:[KPCR_CURRENT_THREAD]
68
69 /* Make sure we're at passive */
70 #if DBG
71 call _KeGetCurrentIrql@0
72 or al, al
73 jz AtPassive
74
75 /* We're not, bugcheck! */
76 push 0
77 push 0
78 push eax
79 push 0
80 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
81 call _KeBugCheckEx@20
82
83 AtPassive:
84
85 /* Make sure that we are not attached and that APCs are not disabled */
86 movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
87 mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
88 or eax, eax
89 jnz InvalidIndex
90 or edx, edx
91 jz ApcsEnabled
92
93 InvalidIndex:
94
95 push 0
96 push edx
97 push eax
98 push 0
99 push APC_INDEX_MISMATCH
100 call _KeBugCheckEx@20
101 ApcsEnabled:
102 #endif
103
104 /* Get the lowest stack limit and check if we can handle it */
105 lea eax, [esp-0x3000]
106 cmp eax, [ebx+KTHREAD_STACK_LIMIT]
107 jnb StackOk
108
109 /* We can't, we'll have to grow our stack */
110 push esp
111 call _MmGrowKernelStack@4
112
113 /* Quit if we failed */
114 or eax, eax
115 jnz GrowFailed
116
117 /* Save the current callback stack */
118 StackOk:
119 push [ebx+KTHREAD_CALLBACK_STACK]
120
121 /* Get and save the trap frame */
122 mov edx, [ebx+KTHREAD_TRAP_FRAME]
123 push edx
124
125 /* Get and save the initial stack */
126 mov esi, [ebx+KTHREAD_INITIAL_STACK]
127 push esi
128
129 /* Set the new callback stack */
130 mov [ebx+KTHREAD_CALLBACK_STACK], esp
131
132 /* Align stack on 16-byte boundary */
133 and esp, ~15
134 mov edi, esp
135
136 /* Set destination and origin NPX Areas */
137 sub esp, NPX_FRAME_LENGTH
138 sub esi, NPX_FRAME_LENGTH
139
140 /* Disable interrupts so we can fill the NPX State */
141 cli
142
143 /* Now copy the NPX State */
144 mov ecx, [esi+FP_CONTROL_WORD]
145 mov [esp+FP_CONTROL_WORD], ecx
146 mov ecx, [esi+FP_STATUS_WORD]
147 mov [esp+FP_STATUS_WORD], ecx
148 mov ecx, [esi+FP_TAG_WORD]
149 mov [esp+FP_TAG_WORD], ecx
150 mov ecx, [esi+FP_DATA_SELECTOR]
151 mov [esp+FP_DATA_SELECTOR], ecx
152 mov ecx, [esi+FN_CR0_NPX_STATE]
153 mov [esp+FN_CR0_NPX_STATE], ecx
154
155 /* Get TSS */
156 mov esi, fs:[KPCR_TSS]
157
158 /* Set the stack address */
159 mov [ebx+KTHREAD_INITIAL_STACK], edi
160
161 /* Bias the stack for V86 mode */
162 mov ecx, esp
163 sub esp, 16
164 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
165 jnz DontBias
166 mov ecx, esp
167
168 DontBias:
169 /* Set new stack address in TSS */
170 mov [esi+KTSS_ESP0], ecx
171
172 /* Allocate the trap frame and set it */
173 sub esp, KTRAP_FRAME_V86_ES
174 mov ebp, esp
175
176 /* Set copy iterator and dest/origin parameters and do the copy */
177 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
178 lea edi, [esp+KTRAP_FRAME_FS]
179 lea esi, [edx+KTRAP_FRAME_FS]
180 rep movsd
181
182 /* Copy DR7 */
183 mov edi, [edx+KTRAP_FRAME_DR7]
184 test edi, ~DR7_RESERVED_MASK
185 mov [esp+KTRAP_FRAME_DR7], edi
186
187 /* Check if we need to save debug registers */
188 jnz SaveDebug
189
190 /* Get user-mode dispatcher address and set it as EIP */
191 SetEip:
192 mov eax, _KeUserCallbackDispatcher
193 mov [esp+KTRAP_FRAME_EIP], eax
194
195 /* Set the exception list */
196 mov eax, fs:[KPCR_EXCEPTION_LIST]
197 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
198
199 /* Set the previous mode */
200 mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
201 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
202
203 /* Bring interrupts back */
204 sti
205
206 /* Exit to user-mode */
207 mov ecx, esp
208 jmp @KiServiceExit@8
209
210 SaveDebug:
211
212 /* Copy all 5 DRs */
213 mov ecx, 5
214 lea edi, [esp+KTRAP_FRAME_DR0]
215 lea esi, [edx+KTRAP_FRAME_DR0]
216 rep movsd
217 jmp SetEip
218
219 GrowFailed:
220 /* Restore registers */
221 pop edi
222 pop esi
223 pop ebx
224 pop ebp
225
226 /* Return */
227 ret 8
228
229 .endfunc
230
231 /*++
232 * @name NtCallbackReturn
233 *
234 * The NtCallbackReturn routine returns to kernel mode after a user-mode
235 * callback was done through KeUserModeCallback. It uses the callback frame
236 * which was setup in order to return the information, restores the stack,
237 * and resumes execution where it was left off.
238 *
239 * @param Result
240 * Pointer to a caller-allocated buffer where the return data
241 * from the user-mode function is located.
242 *
243 * @param ResultLength
244 * Size of the Output Buffer described above.
245 *
246 * @param CallbackStatus
247 * Status code of the callback operation.
248 *
249 * @return Status code of the callback operation.
250 *
251 * @remark This call MUST be paired with KeUserModeCallback.
252 *
253 *--*/
254 .globl _NtCallbackReturn@12
255 .func NtCallbackReturn@12
256 _NtCallbackReturn@12:
257
258 /* Get the current thread and make sure we have a callback stack */
259 mov eax, fs:[KPCR_CURRENT_THREAD]
260 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
261 test ecx, ecx
262 jz NoStack
263
264 /* Get the trap frame */
265 mov ebx, [eax+KTHREAD_TRAP_FRAME]
266
267 /* Restore the exception list */
268 mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
269 mov fs:[KPCR_EXCEPTION_LIST], edx
270
271 /* Get the result, the result length and the status */
272 mov edi, [esp+4]
273 mov esi, [esp+8]
274 mov ebp, [esp+12]
275
276 /* Store the results in the callback stack */
277 mov ebx, [ecx+CBSTACK_RESULT]
278 mov [ebx], edi
279 mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
280 mov [ebx], esi
281
282 /* Get the previous stack */
283 mov ebx, [ecx]
284
285 /* Disable interrupts for NPX save and stack switch */
286 cli
287
288 /* Get the initial stack and restore it */
289 mov esi, [eax+KTHREAD_INITIAL_STACK]
290 mov [eax+KTHREAD_INITIAL_STACK], ebx
291
292 /* Set desination and origin NPX Frames */
293 sub esi, NPX_FRAME_LENGTH
294 sub ebx, NPX_FRAME_LENGTH
295
296 /* Copy NPX Data */
297 mov edx, [esi+FP_CONTROL_WORD]
298 mov [ebx+FP_CONTROL_WORD], edx
299 mov edx, [esi+FP_STATUS_WORD]
300 mov [ebx+FP_STATUS_WORD], edx
301 mov edx, [esi+FP_TAG_WORD]
302 mov [ebx+FP_TAG_WORD], edx
303 mov edx, [esi+FP_DATA_SELECTOR]
304 mov [ebx+FP_DATA_SELECTOR], edx
305 mov edx, [esi+FN_CR0_NPX_STATE]
306 mov [ebx+FN_CR0_NPX_STATE], edx
307
308 /* Check if we failed in user mode */
309 cmp ebp, STATUS_CALLBACK_POP_STACK
310 mov edi, [ecx+CBSTACK_TRAP_FRAME]
311 jz UserFault
312
313 CheckDebug:
314
315 /* Clear DR7 */
316 and dword ptr [edi+KTRAP_FRAME_DR7], 0
317
318 /* Check if debugging was active */
319 test byte ptr [eax+KTHREAD_DEBUG_ACTIVE], 0xFF
320 jnz RestoreDebug
321
322 RestoreStack:
323
324 /* Get TSS */
325 mov edx, fs:[KPCR_TSS]
326
327 /* Restore stack pointer */
328 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
329
330 /* Check if we were in V86 mode */
331 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
332 jnz V86Ret
333 sub ebx, 16
334
335 V86Ret:
336 /* Restore the ESP in TSS */
337 mov [edx+KTSS_ESP0], ebx
338
339 /* Restore the trap frame */
340 mov [eax+KTHREAD_TRAP_FRAME], edi
341
342 /* Bring interrupts back */
343 sti
344
345 /* Restore the callback stack*/
346 pop [eax+KTHREAD_CALLBACK_STACK]
347
348 /* Set status and return */
349 mov eax, ebp
350 pop edi
351 pop esi
352 pop ebx
353 pop ebp
354 pop edx
355
356 /* Clean stack and jump back */
357 add esp, 8
358 jmp edx
359
360 UserFault:
361 /* Set size to copy */
362 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
363
364 /* Check if this was V86 mode */
365 mov esi, [eax+KTHREAD_TRAP_FRAME]
366 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
367
368 /* Save EDI and load destination */
369 mov edx, edi
370 lea edi, [edi+KTRAP_FRAME_FS]
371 jz NotV86
372 add ecx, 16 / 4
373
374 NotV86:
375 /* Set source and copy */
376 lea esi, [esi+KTRAP_FRAME_FS]
377 rep movsd
378
379 /* Restore ECX and ECX */
380 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
381 mov edi, edx
382 jmp CheckDebug
383
384 RestoreDebug:
385 /* Get a pointer to thread's trap frame */
386 mov esi, [eax+KTHREAD_TRAP_FRAME]
387
388 /* Copy debug registers data from it */
389 mov edx, [esi+KTRAP_FRAME_DR0]
390 mov [edi+KTRAP_FRAME_DR0], edx
391 mov edx, [esi+KTRAP_FRAME_DR1]
392 mov [edi+KTRAP_FRAME_DR1], edx
393 mov edx, [esi+KTRAP_FRAME_DR2]
394 mov [edi+KTRAP_FRAME_DR2], edx
395 mov edx, [esi+KTRAP_FRAME_DR3]
396 mov [edi+KTRAP_FRAME_DR3], edx
397 mov edx, [esi+KTRAP_FRAME_DR6]
398 mov [edi+KTRAP_FRAME_DR6], edx
399 mov edx, [esi+KTRAP_FRAME_DR7]
400 mov [edi+KTRAP_FRAME_DR7], edx
401
402 /* Jump back */
403 jmp RestoreStack
404
405 NoStack:
406
407 /* Return failure */
408 mov eax, STATUS_NO_CALLBACK_ACTIVE
409 ret 12
410 .endfunc
411
412 /*++
413 * @name KeSwitchKernelStack
414 *
415 * The KeSwitchKernelStack routine switches from the current thread's stack
416 * to the new specified base and limit.
417 *
418 * @param StackBase
419 * Pointer to the new Stack Base of the thread.
420 *
421 * @param StackLimit
422 * Pointer to the new Stack Limit of the thread.
423 *
424 * @return The previous Stack Base of the thread.
425 *
426 * @remark This routine should typically only be used when converting from a
427 * non-GUI to a GUI Thread. The caller is responsible for freeing the
428 * previous stack. The new stack values MUST be valid before calling
429 * this routine.
430 *
431 *--*/
432 .globl _KeSwitchKernelStack@8
433 .func KeSwitchKernelStack@8
434 _KeSwitchKernelStack@8:
435
436 /* Save volatiles */
437 push esi
438 push edi
439
440 /* Get current thread */
441 mov edx, fs:[KPCR_CURRENT_THREAD]
442
443 /* Get new and current base */
444 mov edi, [esp+12]
445 mov ecx, [edx+KTHREAD_STACK_BASE]
446
447 /* Fixup the frame pointer */
448 sub ebp, ecx
449 add ebp, edi
450
451 /* Fixup the trap frame */
452 mov eax, [edx+KTHREAD_TRAP_FRAME]
453 sub eax, ecx
454 add eax, edi
455 mov [edx+KTHREAD_TRAP_FRAME], eax
456
457 /* Calculate stack size */
458 sub ecx, esp
459
460 /* Get desination and origin */
461 sub edi, ecx
462 mov esi, esp
463
464 /* Save stack pointer */
465 push edi
466
467 /* Copy stack */
468 rep movsb
469
470 /* Restore stack pointer */
471 pop edi
472
473 /* Save old stack base and get new limit/base */
474 mov eax, [edx+KTHREAD_STACK_BASE]
475 mov ecx, [esp+12]
476 mov esi, [esp+16]
477
478 /* Disable interrupts for stack switch */
479 cli
480
481 /* Set new base/limit */
482 mov [edx+KTHREAD_STACK_BASE], ecx
483 mov [edx+KTHREAD_STACK_LIMIT], esi
484
485 /* Set LargeStack */
486 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
487
488 /* Set new initial stack */
489 mov [edx+KTHREAD_INITIAL_STACK], ecx
490
491 /* Get trap frame */
492 mov esi, [edx+KTHREAD_TRAP_FRAME]
493
494 /* Get TSS */
495 mov edx, fs:[KPCR_TSS]
496
497 /* Check if we came from V86 mode */
498 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
499
500 /* Bias for NPX Area */
501 lea ecx, [ecx-NPX_FRAME_LENGTH]
502 jnz V86Switch
503 sub ecx, 16
504
505 V86Switch:
506
507 /* Update ESP in TSS */
508 mov [edx+KTSS_ESP0], ecx
509
510 /* Update stack pointer */
511 mov esp, edi
512
513 /* Bring back interrupts and return */
514 sti
515 pop edi
516 pop esi
517 ret 8
518 .endfunc