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