c957af794be11f9db7e1eaa139f5ec62ddbf1779
[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 ret
34
35 .endfunc
36
37 /*++
38 * @name KiCallUserMode
39 *
40 * The KiSwitchToUserMode routine sets up a Trap Frame and a Callback stack
41 * for the purpose of switching to user mode. The actual final jump is done
42 * by KiServiceExit which will treat this as a syscall return.
43 *
44 * @param OutputBuffer
45 * Pointer to a caller-allocated buffer where to receive the return data
46 * from the user-mode function
47 *
48 * @param OutputLength
49 * Size of the Output Buffer described above.
50 *
51 * @return None. Jumps into KiServiceExit.
52 *
53 * @remark If there is not enough Kernel Stack space, the routine will increase the
54 * Kernel Stack.
55 *
56 * User mode execution resumes at ntdll!KiUserCallbackDispatcher.
57 *
58 * This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
59 *
60 *--*/
61 .globl _KiCallUserMode@8
62 .func KiCallUserMode@8
63 _KiCallUserMode@8:
64
65 /* Save volatile registers */
66 push ebp
67 push ebx
68 push esi
69 push edi
70
71 /* Get the current thread */
72 mov ebx, fs:[KPCR_CURRENT_THREAD]
73
74 /* Make sure we're at passive */
75 #if DBG
76 call _KeGetCurrentIrql@0
77 or al, al
78 jz AtPassive
79
80 /* We're not, bugcheck! */
81 push 0
82 push 0
83 push eax
84 push 0
85 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
86 call _KeBugCheckEx@20
87
88 AtPassive:
89
90 #if 0
91 /* Make sure that we are not attached and that APCs are not disabled */
92 movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
93 mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
94 or eax, eax
95 jnz InvalidIndex
96 or edx, edx
97 jz ApcsEnabled
98
99 InvalidIndex:
100
101 push 0
102 push edx
103 push eax
104 push 0
105 push APC_INDEX_MISMATCH
106 call _KeBugCheckEx@20
107 #endif
108 ApcsEnabled:
109 #endif
110
111 /* Get the lowest stack limit and check if we can handle it */
112 lea eax, [esp-0x3000]
113 cmp eax, [ebx+KTHREAD_STACK_LIMIT]
114 jnb StackOk
115
116 /* We can't, we'll have to grow our stack */
117 push esp
118 call _MmGrowKernelStack@4
119
120 /* Quit if we failed */
121 or eax, eax
122 jnz GrowFailed
123
124 /* Save the current callback stack */
125 StackOk:
126 push [ebx+KTHREAD_CALLBACK_STACK]
127
128 /* Get and save the trap frame */
129 mov edx, [ebx+KTHREAD_TRAP_FRAME]
130 push edx
131
132 /* Get and save the initial stack */
133 mov esi, [ebx+KTHREAD_INITIAL_STACK]
134 push esi
135
136 /* Set the new callback stack */
137 mov [ebx+KTHREAD_CALLBACK_STACK], esp
138
139 /* Align stack on 16-byte boundary */
140 and esp, ~15
141 mov edi, esp
142
143 /* Set destination and origin NPX Areas */
144 sub esp, NPX_FRAME_LENGTH
145 sub esi, NPX_FRAME_LENGTH
146
147 /* Disable interrupts so we can fill the NPX State */
148 cli
149
150 /* Now copy the NPX State */
151 mov ecx, [esi+FN_CONTROL_WORD]
152 mov [esp+FN_CONTROL_WORD], ecx
153 mov ecx, [esi+FN_STATUS_WORD]
154 mov [esp+FN_STATUS_WORD], ecx
155 mov ecx, [esi+FN_TAG_WORD]
156 mov [esp+FN_TAG_WORD], ecx
157 mov ecx, [esi+FN_DATA_SELECTOR]
158 mov [esp+FN_DATA_SELECTOR], ecx
159 mov ecx, [esi+FN_CR0_NPX_STATE]
160 mov [esp+FN_CR0_NPX_STATE], ecx
161
162 /* Get TSS */
163 mov esi, fs:[KPCR_TSS]
164
165 /* Set the stack address */
166 mov [ebx+KTHREAD_INITIAL_STACK], edi
167
168 /* Bias the stack for V86 mode */
169 mov ecx, esp
170 sub esp, 16
171 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
172 jnz DontBias
173 mov ecx, esp
174
175 DontBias:
176 /* Set new stack address in TSS */
177 mov [esi+KTSS_ESP0], ecx
178
179 /* Allocate the trap frame and set it */
180 sub esp, KTRAP_FRAME_V86_ES
181 mov ebp, esp
182
183 /* Set copy iterator and dest/origin parameters and do the copy */
184 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
185 lea edi, [esp+KTRAP_FRAME_FS]
186 lea esi, [edx+KTRAP_FRAME_FS]
187 rep movsd
188
189 /* TODO: Ugly hack because of some missing logic in syscall.s */
190 mov dword ptr [esp+KTRAP_FRAME_GS], 0
191 mov dword ptr [esp+KTRAP_FRAME_DS], KGDT_R3_DATA + RPL_MASK
192 mov dword ptr [esp+KTRAP_FRAME_ES], KGDT_R3_DATA + RPL_MASK
193
194 /* FIXME: Copy debug registers if needed */
195
196 /* Get user-mode dispatcher address and set it as EIP */
197 mov eax, _KeUserCallbackDispatcher
198 mov [esp+KTRAP_FRAME_EIP], eax
199
200 /* Set the exception list */
201 mov eax, fs:[KPCR_EXCEPTION_LIST]
202 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
203
204 /* Set the previous mode */
205 mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
206 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
207
208 /* Bring interrupts back */
209 sti
210
211 /* Write the debug data */
212 mov edi, [ebp+KTRAP_FRAME_EBP]
213 mov edx, [ebp+KTRAP_FRAME_EIP]
214 mov [ebp+KTRAP_FRAME_DEBUGPOINTER], edx
215 mov dword ptr [ebp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
216 mov [ebp+KTRAP_FRAME_DEBUGEBP], ebx
217 mov [ebp+KTRAP_FRAME_DEBUGEIP], edi
218
219 /* Exit to user-mode */
220 jmp _KiServiceExit
221
222 GrowFailed:
223 /* Restore registers */
224 pop edi
225 pop esi
226 pop ebx
227 pop ebp
228
229 /* Return */
230 ret 8
231
232 .endfunc
233
234 /*++
235 * @name NtCallbackReturn
236 *
237 * The NtCallbackReturn routine returns to kernel mode after a user-mode
238 * callback was done through KeUserModeCallback. It uses the callback frame
239 * which was setup in order to return the information, restores the stack,
240 * and resumes execution where it was left off.
241 *
242 * @param Result
243 * Pointer to a caller-allocated buffer where the return data
244 * from the user-mode function is located.
245 *
246 * @param ResultLength
247 * Size of the Output Buffer described above.
248 *
249 * @param CallbackStatus
250 * Status code of the callback operation.
251 *
252 * @return Status code of the callback operation.
253 *
254 * @remark This call MUST be paired with KeUserModeCallback.
255 *
256 *--*/
257 .globl _NtCallbackReturn@12
258 .func NtCallbackReturn@12
259 _NtCallbackReturn@12:
260
261 /* Get the current thread and make sure we have a callback stack */
262 mov eax, fs:[KPCR_CURRENT_THREAD]
263 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
264 test ecx, ecx
265 jz NoStack
266
267 /* Get the trap frame */
268 mov ebx, [eax+KTHREAD_TRAP_FRAME]
269
270 /* Restore the exception list */
271 mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
272 mov fs:[KPCR_EXCEPTION_LIST], edx
273
274 /* Get the result, the result length and the status */
275 mov edi, [esp+4]
276 mov esi, [esp+8]
277 mov ebp, [esp+12]
278
279 /* Store the results in the callback stack */
280 mov ebx, [ecx+CBSTACK_RESULT]
281 mov [ebx], edi
282 mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
283 mov [ebx], esi
284
285 /* Get the previous stack */
286 mov ebx, [ecx]
287
288 /* Disable interrupts for NPX save and stack switch */
289 cli
290
291 /* Get the initial stack and restore it */
292 mov esi, [eax+KTHREAD_INITIAL_STACK]
293 mov [eax+KTHREAD_INITIAL_STACK], ebx
294
295 /* Set desination and origin NPX Frames */
296 sub esi, NPX_FRAME_LENGTH
297 sub ebx, NPX_FRAME_LENGTH
298
299 /* Copy NPX Data */
300 mov edx, [esi+FN_CONTROL_WORD]
301 mov [ebx+FN_CONTROL_WORD], edx
302 mov edx, [esi+FN_STATUS_WORD]
303 mov [ebx+FN_STATUS_WORD], edx
304 mov edx, [esi+FN_TAG_WORD]
305 mov [ebx+FN_TAG_WORD], edx
306 mov edx, [esi+FN_DATA_SELECTOR]
307 mov [ebx+FN_DATA_SELECTOR], edx
308 mov edx, [esi+FN_CR0_NPX_STATE]
309 mov [ebx+FN_CR0_NPX_STATE], edx
310
311 /* Get saved trap frame and clear DR7 */
312 mov edi, [ecx+CBSTACK_TRAP_FRAME]
313 and dword ptr [edi+KTRAP_FRAME_DR7], 0
314
315 /* FIXME: Restore debug regs */
316
317 /* Get TSS */
318 mov edx, fs:[KPCR_TSS]
319
320 /* Restore stack pointer */
321 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
322
323 /* Check if we were in V86 mode */
324 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
325 jnz V86Ret
326 sub ebx, 16
327
328 V86Ret:
329 /* Restore the ESP in TSS */
330 mov [edx+KTSS_ESP0], ebx
331
332 /* Restore the trap frame */
333 mov [eax+KTHREAD_TRAP_FRAME], edi
334
335 /* Bring interrupts back */
336 sti
337
338 /* Restore the callback stack*/
339 pop [eax+KTHREAD_CALLBACK_STACK]
340
341 /* Set status and return */
342 mov eax, ebp
343 pop edi
344 pop esi
345 pop ebx
346 pop ebp
347 pop edx
348
349 /* Clean stack and jump back */
350 add esp, 8
351 jmp edx
352
353 NoStack:
354
355 /* Return failure */
356 mov eax, STATUS_NO_CALLBACK_ACTIVE
357 ret 12
358
359 .endfunc
360
361 .globl _KeSwitchKernelStack@8
362 .func KeSwitchKernelStack@8
363 _KeSwitchKernelStack@8:
364
365 /* Save volatiles */
366 push esi
367 push edi
368
369 /* Get current thread */
370 mov edx, fs:[KPCR_CURRENT_THREAD]
371
372 /* Get new and current base */
373 mov edi, [esp+12]
374 mov ecx, [edx+KTHREAD_STACK_BASE]
375
376 /* Fixup the frame pointer */
377 sub ebp, ecx
378 add ebp, edi
379
380 /* Fixup the trap frame */
381 mov eax, [edx+KTHREAD_TRAP_FRAME]
382 sub eax, ecx
383 add eax, edi
384 mov [edx+KTHREAD_TRAP_FRAME], eax
385
386 /* Calculate stack size */
387 sub ecx, esp
388
389 /* Get desination and origin */
390 sub edi, ecx
391 mov esi, esp
392
393 /* Save stack pointer */
394 push edi
395
396 /* Copy stack */
397 rep movsb
398
399 /* Restore stack pointer */
400 pop edi
401
402 /* Save old stack base and get new limit/base */
403 mov eax, [edx+KTHREAD_STACK_BASE]
404 mov ecx, [esp+12]
405 mov esi, [esp+16]
406
407 /* Disable interrupts for stack switch */
408 cli
409
410 /* Set new base/limit */
411 mov [edx+KTHREAD_STACK_BASE], ecx
412 mov [edx+KTHREAD_STACK_LIMIT], esi
413
414 /* Set LargeStack */
415 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
416
417 /* Set new initial stack */
418 mov [edx+KTHREAD_INITIAL_STACK], ecx
419
420 /* Get trap frame */
421 mov esi, [edx+KTHREAD_TRAP_FRAME]
422
423 /* Get TSS */
424 mov edx, fs:[KPCR_TSS]
425
426 /* Check if we came from V86 mode */
427 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
428
429 /* Bias for NPX Area */
430 lea ecx, [ecx-NPX_FRAME_LENGTH]
431 jnz V86Switch
432 sub ecx, 16
433
434 V86Switch:
435
436 /* Update ESP in TSS */
437 mov [edx+KTSS_ESP0], ecx
438
439 /* Update stack pointer */
440 mov esp, edi
441
442 /* Bring back interrupts and return */
443 sti
444 pop edi
445 pop esi
446 ret 8
447
448 .endfunc