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