- Fix some bugs in KiCallUserMode (as of yet unused).
[reactos.git] / reactos / 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.h>
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
17
18 // This file is a work in progress. Most of the code is currently disabled.
19
20 /* GLOBALS ****************************************************************/
21 .extern PVOID _KeUserCallbackDispatcher
22
23 /* FUNCTIONS ****************************************************************/
24
25 .globl _KiGetUserModeStackAddress@0
26 .func KiGetUserModeStackAddress@0
27 _KiGetUserModeStackAddress@0:
28
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]
33
34 .endfunc
35
36 /*++
37 * @name KiCallUserMode
38 *
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.
42 *
43 * @param OutputBuffer
44 * Pointer to a caller-allocated buffer where to receive the return data
45 * from the user-mode function
46 *
47 * @param OutputLength
48 * Size of the Output Buffer described above.
49 *
50 * @return None. Jumps into KiServiceExit.
51 *
52 * @remark If there is not enough Kernel Stack space, the routine will increase the
53 * Kernel Stack.
54 *
55 * User mode execution resumes at ntdll!KiUserCallbackDispatcher.
56 *
57 * This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
58 *
59 *--*/
60 .globl _KiCallUserMode@8
61 .func KiCallUserMode@8
62 _KiCallUserMode@8:
63
64 /* Save volatile registers */
65 push ebp
66 push ebx
67 push esi
68 push edi
69
70 /* Get the current thread */
71 mov ebx, fs:[KPCR_CURRENT_THREAD]
72
73 /* Make sure we're at passive */
74 #if DBG
75 call _KeGetCurrentIrql@0
76 or al, al
77 jz AtPassive
78
79 /* We're not, bugcheck! */
80 push 0
81 push 0
82 push eax
83 push 0
84 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
85 call _KeBugCheckEx@20
86
87 AtPassive:
88
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]
92 or eax, eax
93 jnz InvalidIndex
94 or edx, edx
95 jz ApcsEnabled
96
97 InvalidIndex:
98
99 push 0
100 push edx
101 push eax
102 push 0
103 push APC_INDEX_MISMATCH
104 call _KeBugCheckEx@20
105
106 ApcsEnabled:
107 #endif
108
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]
112 jnb StackOk
113
114 /* We can't, we'll have to grow our stack */
115 push esp
116 call _MmGrowKernelStack@4
117
118 /* Quit if we failed */
119 or eax, eax
120 jnz GrowFailed
121
122 /* Save the current callback stack */
123 StackOk:
124 push [ebx+KTHREAD_CALLBACK_STACK]
125
126 /* Get and save the trap frame */
127 mov edx, [ebx+KTHREAD_TRAP_FRAME]
128 push edx
129
130 /* Get and save the initial stack */
131 mov esi, [ebx+KTHREAD_INITIAL_STACK]
132 push esi
133
134 /* Set the new callback stack */
135 mov [ebx+KTHREAD_CALLBACK_STACK], esp
136
137 /* Align stack on 16-byte boundary */
138 and esp, ~16
139 mov edi, esp
140
141 /* Set destination and origin NPX Areas */
142 sub esp, NPX_FRAME_LENGTH
143 sub esi, NPX_FRAME_LENGTH
144
145 /* Disable interrupts so we can fill the NPX State */
146 cli
147
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
159
160 /* Get TSS */
161 mov esi, fs:[KPCR_TSS]
162
163 /* Set the stack address */
164 mov [ebx+KTHREAD_INITIAL_STACK], edi
165
166 /* Bias the stack for V86 mode */
167 mov ecx, esp
168 sub esp, 16
169 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
170 jnz DontBias
171 mov ecx, esp
172
173 DontBias:
174 /* Set new stack address in TSS */
175 mov [esi+KTSS_ESP0], ecx
176
177 /* Allocate the trap frame and set it */
178 sub esp, KTRAP_FRAME_V86_ES
179 mov ebp, esp
180
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]
185 rep movsd
186
187 /* FIXME: Copy debug registers if needed */
188
189 /* Get user-mode dispatcher address and set it as EIP */
190 mov eax, _KeUserCallbackDispatcher
191 mov [esp+KTRAP_FRAME_EIP], eax
192
193 /* Set the exception list */
194 mov eax, [KPCR_EXCEPTION_LIST]
195 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
196
197 /* Set the previous mode */
198 mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
199 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
200
201 /* Bring interrupts back */
202 sti
203
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
211
212 /* Exit to user-mode */
213 jmp _KiServiceExit
214
215 GrowFailed:
216 /* Restore registers */
217 pop edi
218 pop esi
219 pop ebx
220 pop ebp
221
222 /* Return */
223 ret 8
224
225 .endfunc
226
227 /*++
228 * @name NtCallbackReturn
229 *
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.
234 *
235 * @param Result
236 * Pointer to a caller-allocated buffer where the return data
237 * from the user-mode function is located.
238 *
239 * @param ResultLength
240 * Size of the Output Buffer described above.
241 *
242 * @param CallbackStatus
243 * Status code of the callback operation.
244 *
245 * @return Status code of the callback operation.
246 *
247 * @remark This call MUST be paired with KeUserModeCallback.
248 *
249 *--*/
250 .globl _NtCallbackReturn2@12
251 .func NtCallbackReturn2@12
252 _NtCallbackReturn2@12:
253
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]
258 test ecx, ecx
259 jz NoStack
260
261 /* Get the trap frame */
262 mov ebx, [eax+KTHREAD_TRAP_FRAME]
263
264 /* Restore the exception list */
265 mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
266 mov fs:[KPCR_EXCEPTION_LIST], edx
267
268 /* Get the result, the result length and the status */
269 mov edi, [esp+4]
270 mov esi, [esp+8]
271 mov ebp, [esp+12]
272
273 /* Store the results in the callback stack */
274 mov ebx, [ecx+CBSTACK_RESULT]
275 mov [ebx], edi
276 mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
277 mov [ebx], esi
278
279 /* Get the previous stack */
280 mov ebx, [ecx]
281
282 /* Disable interrupts for NPX save and stack switch */
283 cli
284
285 /* Get the initial stack and restore it */
286 mov esi, fs:[KPCR_INITIAL_STACK]
287 mov [eax+KTHREAD_INITIAL_STACK], ebx
288
289 /* Set desination and origin NPX Frames */
290 sub esi, NPX_FRAME_LENGTH
291 sub ebx, NPX_FRAME_LENGTH
292
293 /* Copy NPX Data */
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
304
305 /* Get saved trap frame and clear DR7 */
306 mov edi, [ecx+CBSTACK_TRAP_FRAME]
307 and dword ptr [edi+KTRAP_FRAME_DR7], 0
308
309 /* FIXME: Restore debug regs */
310
311 /* Get TSS */
312 mov edx, fs:[KPCR_TSS]
313
314 /* Restore stack pointer */
315 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
316
317 /* Check if we were in V86 mode */
318 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
319 jnz V86Ret
320 sub ebx, 16
321
322 V86Ret:
323 /* Restore the ESP in TSS */
324 mov [edx+KTSS_ESP0], ebx
325
326 /* Restore the trap frame */
327 mov [eax+KTHREAD_TRAP_FRAME], edi
328
329 /* Bring interrupts back */
330 sti
331
332 /* Restore the callback stack*/
333 pop [eax+KTHREAD_CALLBACK_STACK]
334
335 /* Set status and return */
336 mov eax, ebp
337 pop edi
338 pop esi
339 pop ebx
340 pop ebp
341 pop edx
342
343 /* Clean stack and jump back */
344 add esp, 8
345 jmp edx
346
347 NoStack:
348
349 /* Return failure */
350 mov eax, STATUS_NO_CALLBACK_ACTIVE
351 ret 12
352
353 .endfunc
354
355 .globl _KeSwitchKernelStack@8
356 .func KeSwitchKernelStack@8
357 _KeSwitchKernelStack@8:
358
359 /* Save volatiles */
360 push esi
361 push edi
362
363 /* Get current thread */
364 mov edx, fs:[KPCR_CURRENT_THREAD]
365
366 /* Get new and current base */
367 mov edi, [esp+12]
368 mov ecx, [edx+KTHREAD_STACK_BASE]
369
370 /* Fixup the frame pointer */
371 sub ebp, ecx
372 add ebp, edi
373
374 /* Fixup the trap frame */
375 mov eax, [edx+KTHREAD_TRAP_FRAME]
376 sub eax, ecx
377 add eax, edi
378 mov [edx+KTHREAD_TRAP_FRAME], eax
379
380 /* Calculate stack size */
381 sub ecx, esp
382
383 /* Get desination and origin */
384 sub edi, ecx
385 mov esi, esp
386
387 /* Save stack pointer */
388 push edi
389
390 /* Copy stack */
391 rep movsb
392
393 /* Restore stack pointer */
394 pop edi
395
396 /* Save old stack base and get new limit/base */
397 mov eax, [edx+KTHREAD_STACK_BASE]
398 mov ecx, [esp+12]
399 mov esi, [esp+16]
400
401 /* Disable interrupts for stack switch */
402 cli
403
404 /* Set new base/limit */
405 mov [edx+KTHREAD_STACK_BASE], ecx
406 mov [edx+KTHREAD_STACK_LIMIT], esi
407
408 /* Set LargeStack */
409 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
410
411 /* Set new initial stack */
412 mov [edx+KTHREAD_INITIAL_STACK], ecx
413
414 /* Get trap frame */
415 mov esi, [edx+KTHREAD_TRAP_FRAME]
416
417 /* Get TSS */
418 mov edx, fs:[KPCR_TSS]
419
420 /* Check if we came from V86 mode */
421 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
422
423 /* Bias for NPX Area */
424 lea ecx, [ecx-NPX_FRAME_LENGTH]
425 jnz V86Switch
426 sub ecx, 16
427
428 V86Switch:
429
430 /* Update ESP in TSS */
431 mov [edx+KTSS_ESP0], ecx
432
433 /* Update stack pointer */
434 mov esp, edi
435
436 /* Bring back interrupts and return */
437 sti
438 pop edi
439 pop esi
440 ret 8
441
442 .endfunc