e4d263733cee6c1d3a1dbca4216261ff8c5bab1d
[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 call _KeGetCurrentIrql@0
75 or al, al
76 jz AtPassive
77
78 /* We're not, bugcheck! */
79 push 0
80 push 0
81 push eax
82 push 0
83 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
84 call _KeBugCheckEx@20
85
86 AtPassive:
87
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]
91 or eax, eax
92 jz InvalidIndex
93 or edx, edx
94 jz ApcsEnabled
95
96 InvalidIndex:
97
98 push 0
99 push edx
100 push eax
101 push 0
102 push APC_INDEX_MISMATCH
103 call _KeBugCheckEx@20
104
105 ApcsEnabled:
106
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]
110 jnb StackOk
111
112 /* We can't, we'll have to grow our stack */
113 push esp
114 call _MmGrowKernelStack@4
115
116 /* Quit if we failed */
117 or eax, eax
118 jnz GrowFailed
119
120 /* Save the current callback stack */
121 StackOk:
122 push [ebx+KTHREAD_CALLBACK_STACK]
123
124 /* Get and save the trap frame */
125 mov edx, [ebx+KTHREAD_TRAP_FRAME]
126 push edx
127
128 /* Get and save the initial stack */
129 mov esi, [ebx+KTHREAD_INITIAL_STACK]
130 push esi
131
132 /* Set the new callback stack */
133 mov [ebx+KTHREAD_CALLBACK_STACK], esp
134
135 /* Align stack on 16-byte boundary */
136 and esp, ~16
137 mov edi, esp
138
139 /* Set destination and origin NPX Areas */
140 sub esp, NPX_FRAME_LENGTH
141 sub esi, NPX_FRAME_LENGTH
142
143 /* Disable interrupts so we can fill the NPX State */
144 cli
145
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
157
158 /* Get TSS */
159 mov esi, fs:[KPCR_TSS]
160
161 /* Set the stack address */
162 mov [ebx+KTHREAD_INITIAL_STACK], edi
163
164 /* Bias the stack for V86 mode */
165 mov ecx, esp
166 sub esp, 16
167 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
168 jnz DontBias
169 mov ecx, esp
170
171 DontBias:
172 /* Set new stack address in TSS */
173 mov [esi+KTSS_ESP0], ecx
174
175 /* Allocate the trap frame and set it */
176 sub esp, KTRAP_FRAME_V86_ES
177 mov ebp, esp
178
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]
183 rep movsd
184
185 /* FIXME: Copy debug registers if needed */
186
187 /* Get user-mode dispatcher address and set it as EIP */
188 mov eax, _KeUserCallbackDispatcher
189 mov [esp+KTRAP_FRAME_EIP], eax
190
191 /* Set the exception list */
192 mov eax, [KPCR_EXCEPTION_LIST]
193 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
194
195 /* Set the previous mode */
196 mov eax, [EDX+KTRAP_FRAME_PREVIOUS_MODE]
197 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
198
199 /* Bring interrupts back */
200 sti
201
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
209
210 /* Exit to user-mode */
211 jmp _KiServiceExit
212
213 GrowFailed:
214 /* Restore registers */
215 pop edi
216 pop esi
217 pop ebx
218 pop ebp
219
220 /* Return */
221 ret 8
222
223 .endfunc
224
225 /*++
226 * @name NtCallbackReturn
227 *
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.
232 *
233 * @param Result
234 * Pointer to a caller-allocated buffer where the return data
235 * from the user-mode function is located.
236 *
237 * @param ResultLength
238 * Size of the Output Buffer described above.
239 *
240 * @param CallbackStatus
241 * Status code of the callback operation.
242 *
243 * @return Status code of the callback operation.
244 *
245 * @remark This call MUST be paired with KeUserModeCallback.
246 *
247 *--*/
248 .globl _NtCallbackReturn2@12
249 .func NtCallbackReturn2@12
250 _NtCallbackReturn2@12:
251
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]
255 test ecx, ecx
256 jz NoStack
257
258 /* Get the trap frame */
259 mov ebx, [eax+KTHREAD_TRAP_FRAME]
260
261 /* Restore the exception list */
262 mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
263 mov fs:[KPCR_EXCEPTION_LIST], edx
264
265 /* Get the result, the result length and the status */
266 mov edi, [esp+4]
267 mov esi, [esp+8]
268 mov ebp, [esp+12]
269
270 /* Store the results in the callback stack */
271 mov ebx, [ecx+CBSTACK_RESULT]
272 mov [ebx], edi
273 mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
274 mov [ebx], esi
275
276 /* Get the previous stack */
277 mov ebx, [ecx]
278
279 /* Disable interrupts for NPX save and stack switch */
280 cli
281
282 /* Get the initial stack and restore it */
283 mov esi, fs:[KPCR_INITIAL_STACK]
284 mov [eax+KTHREAD_INITIAL_STACK], ebx
285
286 /* Set desination and origin NPX Frames */
287 sub esi, NPX_FRAME_LENGTH
288 sub ebx, NPX_FRAME_LENGTH
289
290 /* Copy NPX Data */
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
301
302 /* Get saved trap frame and clear DR7 */
303 mov edi, [ecx+CBSTACK_TRAP_FRAME]
304 and dword ptr [edi+KTRAP_FRAME_DR7], 0
305
306 /* FIXME: Restore debug regs */
307
308 /* Get TSS */
309 mov edx, fs:[KPCR_TSS]
310
311 /* Restore stack pointer */
312 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
313
314 /* Check if we were in V86 mode */
315 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
316 jnz V86Ret
317 sub ebx, 16
318
319 V86Ret:
320 /* Restore the ESP in TSS */
321 mov [edx+KTSS_ESP0], ebx
322
323 /* Restore the trap frame */
324 mov [eax+KTHREAD_TRAP_FRAME], edi
325
326 /* Bring interrupts back */
327 sti
328
329 /* Restore the callback stack*/
330 pop [eax+KTHREAD_CALLBACK_STACK]
331
332 /* Set status and return */
333 mov eax, ebp
334 pop edi
335 pop esi
336 pop ebx
337 pop ebp
338 pop edx
339
340 /* Clean stack and jump back */
341 add esp, 8
342 jmp edx
343
344 NoStack:
345
346 /* Return failure */
347 mov eax, STATUS_NO_CALLBACK_ACTIVE
348 ret 12
349
350 .endfunc
351
352 .globl _KeSwitchKernelStack@8
353 .func KeSwitchKernelStack@8
354 _KeSwitchKernelStack@8:
355
356 /* Save volatiles */
357 push esi
358 push edi
359
360 /* Get current thread */
361 mov edx, fs:[KPCR_CURRENT_THREAD]
362
363 /* Get new and current base */
364 mov edi, [esp+12]
365 mov ecx, [edx+KTHREAD_STACK_BASE]
366
367 /* Fixup the frame pointer */
368 sub ebp, ecx
369 add ebp, edi
370
371 /* Fixup the trap frame */
372 mov eax, [edx+KTHREAD_TRAP_FRAME]
373 sub eax, ecx
374 add eax, edi
375 mov [edx+KTHREAD_TRAP_FRAME], eax
376
377 /* Calculate stack size */
378 sub ecx, esp
379
380 /* Get desination and origin */
381 sub edi, ecx
382 mov esi, esp
383
384 /* Save stack pointer */
385 push edi
386
387 /* Copy stack */
388 rep movsb
389
390 /* Restore stack pointer */
391 pop edi
392
393 /* Save old stack base and get new limit/base */
394 mov eax, [edx+KTHREAD_STACK_BASE]
395 mov ecx, [esp+12]
396 mov esi, [esp+16]
397
398 /* Disable interrupts for stack switch */
399 cli
400
401 /* Set new base/limit */
402 mov [edx+KTHREAD_STACK_BASE], ecx
403 mov [edx+KTHREAD_STACK_LIMIT], esi
404
405 /* Set LargeStack */
406 mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
407
408 /* Set new initial stack */
409 mov [edx+KTHREAD_INITIAL_STACK], ecx
410
411 /* Get trap frame */
412 mov esi, [edx+KTHREAD_TRAP_FRAME]
413
414 /* Get TSS */
415 mov edx, fs:[KPCR_TSS]
416
417 /* Check if we came from V86 mode */
418 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
419
420 /* Bias for NPX Area */
421 lea ecx, [ecx-NPX_FRAME_LENGTH]
422 jnz V86Switch
423 sub ecx, 16
424
425 V86Switch:
426
427 /* Update ESP in TSS */
428 mov [edx+KTSS_ESP0], ecx
429
430 /* Update stack pointer */
431 mov esp, edi
432
433 /* Bring back interrupts and return */
434 sti
435 pop edi
436 pop esi
437 ret 8
438
439 .endfunc