[NTOS]: Implement the KiInterruptTemplate in C since we now have a C entrry macro...
[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 <internal/i386/asmmacro.S>
13 .intel_syntax noprefix
14
15 /* FUNCTIONS ****************************************************************/
16
17 .globl _KiGetUserModeStackAddress@0
18 .func KiGetUserModeStackAddress@0
19 _KiGetUserModeStackAddress@0:
20
21 /* Get the current thread's trapframe and return the esp */
22 mov eax, fs:[KPCR_CURRENT_THREAD]
23 mov eax, [eax+KTHREAD_TRAP_FRAME]
24 lea eax, [eax+KTRAP_FRAME_ESP]
25 ret
26
27 .endfunc
28
29 /*++
30 * @name KiCallUserMode
31 *
32 * The KiSwitchToUserMode routine sets up a Trap Frame and a Callback stack
33 * for the purpose of switching to user mode. The actual final jump is done
34 * by KiServiceExit which will treat this as a syscall return.
35 *
36 * @param OutputBuffer
37 * Pointer to a caller-allocated buffer where to receive the return data
38 * from the user-mode function
39 *
40 * @param OutputLength
41 * Size of the Output Buffer described above.
42 *
43 * @return None. Jumps into KiServiceExit.
44 *
45 * @remark If there is not enough Kernel Stack space, the routine will increase the
46 * Kernel Stack.
47 *
48 * User mode execution resumes at ntdll!KiUserCallbackDispatcher.
49 *
50 * This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
51 *
52 *--*/
53 .globl _KiCallUserMode@8
54 .func KiCallUserMode@8
55 _KiCallUserMode@8:
56
57 /* Save volatile registers */
58 push ebp
59 push ebx
60 push esi
61 push edi
62
63 /* Get the current thread */
64 mov ebx, fs:[KPCR_CURRENT_THREAD]
65
66 /* Make sure we're at passive */
67 #if DBG
68 call _KeGetCurrentIrql@0
69 or al, al
70 jz AtPassive
71
72 /* We're not, bugcheck! */
73 push 0
74 push 0
75 push eax
76 push 0
77 push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
78 call _KeBugCheckEx@20
79
80 AtPassive:
81
82 /* Make sure that we are not attached and that APCs are not disabled */
83 movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
84 mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
85 or eax, eax
86 jnz InvalidIndex
87 or edx, edx
88 jz ApcsEnabled
89
90 InvalidIndex:
91
92 push 0
93 push edx
94 push eax
95 push 0
96 push APC_INDEX_MISMATCH
97 call _KeBugCheckEx@20
98 ApcsEnabled:
99 #endif
100
101 /* Get the lowest stack limit and check if we can handle it */
102 lea eax, [esp-0x3000]
103 cmp eax, [ebx+KTHREAD_STACK_LIMIT]
104 jnb StackOk
105
106 /* We can't, we'll have to grow our stack */
107 push esp
108 call _MmGrowKernelStack@4
109
110 /* Quit if we failed */
111 or eax, eax
112 jnz GrowFailed
113
114 /* Save the current callback stack */
115 StackOk:
116 push [ebx+KTHREAD_CALLBACK_STACK]
117
118 /* Get and save the trap frame */
119 mov edx, [ebx+KTHREAD_TRAP_FRAME]
120 push edx
121
122 /* Get and save the initial stack */
123 mov esi, [ebx+KTHREAD_INITIAL_STACK]
124 push esi
125
126 /* Set the new callback stack */
127 mov [ebx+KTHREAD_CALLBACK_STACK], esp
128
129 /* Align stack on 16-byte boundary */
130 and esp, ~15
131 mov edi, esp
132
133 /* Set destination and origin NPX Areas */
134 sub esp, NPX_FRAME_LENGTH
135 sub esi, NPX_FRAME_LENGTH
136
137 /* Disable interrupts so we can fill the NPX State */
138 cli
139
140 /* Now copy the NPX State */
141 mov ecx, [esi+FP_CONTROL_WORD]
142 mov [esp+FP_CONTROL_WORD], ecx
143 mov ecx, [esi+FP_STATUS_WORD]
144 mov [esp+FP_STATUS_WORD], ecx
145 mov ecx, [esi+FP_TAG_WORD]
146 mov [esp+FP_TAG_WORD], ecx
147 mov ecx, [esi+FP_DATA_SELECTOR]
148 mov [esp+FP_DATA_SELECTOR], ecx
149 mov ecx, [esi+FN_CR0_NPX_STATE]
150 mov [esp+FN_CR0_NPX_STATE], ecx
151
152 /* Get TSS */
153 mov esi, fs:[KPCR_TSS]
154
155 /* Set the stack address */
156 mov [ebx+KTHREAD_INITIAL_STACK], edi
157
158 /* Bias the stack for V86 mode */
159 mov ecx, esp
160 sub esp, 16
161 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
162 jnz DontBias
163 mov ecx, esp
164
165 DontBias:
166 /* Set new stack address in TSS */
167 mov [esi+KTSS_ESP0], ecx
168
169 /* Allocate the trap frame and set it */
170 sub esp, KTRAP_FRAME_V86_ES
171 mov ebp, esp
172
173 /* Set copy iterator and dest/origin parameters and do the copy */
174 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
175 lea edi, [esp+KTRAP_FRAME_FS]
176 lea esi, [edx+KTRAP_FRAME_FS]
177 rep movsd
178
179 /* Copy DR7 */
180 mov edi, [edx+KTRAP_FRAME_DR7]
181 test edi, ~DR7_RESERVED_MASK
182 mov [esp+KTRAP_FRAME_DR7], edi
183
184 /* Check if we need to save debug registers */
185 jnz SaveDebug
186
187 /* Get user-mode dispatcher address and set it as EIP */
188 SetEip:
189 mov eax, _KeUserCallbackDispatcher
190 mov [esp+KTRAP_FRAME_EIP], eax
191
192 /* Set the exception list */
193 mov eax, fs:[KPCR_EXCEPTION_LIST]
194 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
195
196 /* Set the previous mode */
197 mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
198 mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
199
200 /* Bring interrupts back */
201 sti
202
203 /* Exit to user-mode */
204 mov ecx, esp
205 jmp @KiServiceExit@8
206
207 SaveDebug:
208
209 /* Copy all 5 DRs */
210 mov ecx, 5
211 lea edi, [esp+KTRAP_FRAME_DR0]
212 lea esi, [edx+KTRAP_FRAME_DR0]
213 rep movsd
214 jmp SetEip
215
216 GrowFailed:
217 /* Restore registers */
218 pop edi
219 pop esi
220 pop ebx
221 pop ebp
222
223 /* Return */
224 ret 8
225
226 .endfunc
227
228 /*++
229 * @name NtCallbackReturn
230 *
231 * The NtCallbackReturn routine returns to kernel mode after a user-mode
232 * callback was done through KeUserModeCallback. It uses the callback frame
233 * which was setup in order to return the information, restores the stack,
234 * and resumes execution where it was left off.
235 *
236 * @param Result
237 * Pointer to a caller-allocated buffer where the return data
238 * from the user-mode function is located.
239 *
240 * @param ResultLength
241 * Size of the Output Buffer described above.
242 *
243 * @param CallbackStatus
244 * Status code of the callback operation.
245 *
246 * @return Status code of the callback operation.
247 *
248 * @remark This call MUST be paired with KeUserModeCallback.
249 *
250 *--*/
251 .globl _NtCallbackReturn@12
252 .func NtCallbackReturn@12
253 _NtCallbackReturn@12:
254
255 /* Get the current thread and make sure we have a callback stack */
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, [eax+KTHREAD_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+FP_CONTROL_WORD]
295 mov [ebx+FP_CONTROL_WORD], edx
296 mov edx, [esi+FP_STATUS_WORD]
297 mov [ebx+FP_STATUS_WORD], edx
298 mov edx, [esi+FP_TAG_WORD]
299 mov [ebx+FP_TAG_WORD], edx
300 mov edx, [esi+FP_DATA_SELECTOR]
301 mov [ebx+FP_DATA_SELECTOR], edx
302 mov edx, [esi+FN_CR0_NPX_STATE]
303 mov [ebx+FN_CR0_NPX_STATE], edx
304
305 /* Check if we failed in user mode */
306 cmp ebp, STATUS_CALLBACK_POP_STACK
307 mov edi, [ecx+CBSTACK_TRAP_FRAME]
308 jz UserFault
309
310 CheckDebug:
311
312 /* Clear DR7 */
313 and dword ptr [edi+KTRAP_FRAME_DR7], 0
314
315 /* Check if debugging was active */
316 test byte ptr [eax+KTHREAD_DEBUG_ACTIVE], 0xFF
317 jnz RestoreDebug
318
319 RestoreStack:
320
321 /* Get TSS */
322 mov edx, fs:[KPCR_TSS]
323
324 /* Restore stack pointer */
325 lea esp, [ecx+CBSTACK_CALLBACK_STACK]
326
327 /* Check if we were in V86 mode */
328 test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
329 jnz V86Ret
330 sub ebx, 16
331
332 V86Ret:
333 /* Restore the ESP in TSS */
334 mov [edx+KTSS_ESP0], ebx
335
336 /* Restore the trap frame */
337 mov [eax+KTHREAD_TRAP_FRAME], edi
338
339 /* Bring interrupts back */
340 sti
341
342 /* Restore the callback stack*/
343 pop [eax+KTHREAD_CALLBACK_STACK]
344
345 /* Set status and return */
346 mov eax, ebp
347 pop edi
348 pop esi
349 pop ebx
350 pop ebp
351 pop edx
352
353 /* Clean stack and jump back */
354 add esp, 8
355 jmp edx
356
357 UserFault:
358 /* Set size to copy */
359 mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
360
361 /* Check if this was V86 mode */
362 mov esi, [eax+KTHREAD_TRAP_FRAME]
363 test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
364
365 /* Save EDI and load destination */
366 mov edx, edi
367 lea edi, [edi+KTRAP_FRAME_FS]
368 jz NotV86
369 add ecx, 16 / 4
370
371 NotV86:
372 /* Set source and copy */
373 lea esi, [esi+KTRAP_FRAME_FS]
374 rep movsd
375
376 /* Restore ECX and ECX */
377 mov ecx, [eax+KTHREAD_CALLBACK_STACK]
378 mov edi, edx
379 jmp CheckDebug
380
381 RestoreDebug:
382 /* Get a pointer to thread's trap frame */
383 mov esi, [eax+KTHREAD_TRAP_FRAME]
384
385 /* Copy debug registers data from it */
386 mov edx, [esi+KTRAP_FRAME_DR0]
387 mov [edi+KTRAP_FRAME_DR0], edx
388 mov edx, [esi+KTRAP_FRAME_DR1]
389 mov [edi+KTRAP_FRAME_DR1], edx
390 mov edx, [esi+KTRAP_FRAME_DR2]
391 mov [edi+KTRAP_FRAME_DR2], edx
392 mov edx, [esi+KTRAP_FRAME_DR3]
393 mov [edi+KTRAP_FRAME_DR3], edx
394 mov edx, [esi+KTRAP_FRAME_DR6]
395 mov [edi+KTRAP_FRAME_DR6], edx
396 mov edx, [esi+KTRAP_FRAME_DR7]
397 mov [edi+KTRAP_FRAME_DR7], edx
398
399 /* Jump back */
400 jmp RestoreStack
401
402 NoStack:
403
404 /* Return failure */
405 mov eax, STATUS_NO_CALLBACK_ACTIVE
406 ret 12
407 .endfunc
408
409 /*++
410 * @name KeSwitchKernelStack
411 *
412 * The KeSwitchKernelStack routine switches from the current thread's stack
413 * to the new specified base and limit.
414 *
415 * @param StackBase
416 * Pointer to the new Stack Base of the thread.
417 *
418 * @param StackLimit
419 * Pointer to the new Stack Limit of the thread.
420 *
421 * @return The previous Stack Base of the thread.
422 *
423 * @remark This routine should typically only be used when converting from a
424 * non-GUI to a GUI Thread. The caller is responsible for freeing the
425 * previous stack. The new stack values MUST be valid before calling
426 * this routine.
427 *
428 *--*/
429 .globl _KeSwitchKernelStack@8
430 .func 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 .endfunc