Large partial cleanup of ntoskrnl internal headers, mostly to delete obsoleted or...
[reactos.git] / reactos / ntoskrnl / ke / i386 / syscall.S
1 /* $Id$
2 *
3 * FILE: ntoskrnl/ke/i386/syscall.S
4 * COPYRIGHT: See COPYING in the top level directory
5 * PURPOSE: System Call Handler
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
7 * UPDATE HISTORY:
8 * ??-??-??: Original Version - David Welch(?)
9 * 13-01-05: Complete rewrite, added support for SYSENTER, direct kmode syscalls
10 * and re-wrote most of handler code. - Alex Ionescu
11 */
12
13 #include <roscfg.h>
14 #include <internal/i386/ke.h>
15 #include <internal/asm.h>
16 #include <ndk/i386/segment.h>
17 #include <napi/shared_data.h>
18
19 #define UserMode (1)
20
21 .globl KeReturnFromSystemCallWithHook
22 .globl _KiServiceExit
23 .globl _KiServiceExit2
24 .globl _KiFastCallEntry
25 .globl _KiSystemService
26 .globl _KiDebugService
27
28 _KiFastCallEntry:
29
30 /* Set FS to PCR */
31 movl $PCR_SELECTOR, %ecx
32 movw %cx, %fs
33
34 /* Set the current stack to Kernel Stack */
35 movl %fs:KPCR_TSS, %ecx
36 movl %ss:KTSS_ESP0(%ecx), %ecx
37 movl %ecx, %esp
38
39 /* Set up a fake INT Stack. */
40 pushl $USER_DS
41 pushl %edx /* Ring 3 SS:ESP */
42 pushfl
43 orl $X86_EFLAGS_IF, (%esp) /* Re-enable IRQs in EFLAGS, to fake INT */
44 pushl $USER_CS
45 pushl $KUSER_SHARED_SYSCALL_RET
46
47 /* User Parameter List */
48 add $8, %edx
49
50 _KiSystemService:
51
52 /*
53 * Construct a trap frame on the stack.
54 * The following are already on the stack.
55 */
56 // SS + 0x0
57 // ESP + 0x4
58 // EFLAGS + 0x8
59 // CS + 0xC
60 // EIP + 0x10
61 pushl $0 // + 0x14
62 pushl %ebp // + 0x18
63 pushl %ebx // + 0x1C
64 pushl %esi // + 0x20
65 pushl %edi // + 0x24
66 pushl %fs // + 0x28
67
68 /* Load PCR Selector into fs */
69 movw $PCR_SELECTOR, %bx
70 movw %bx, %fs
71
72 /* Save the previous exception list */
73 pushl %fs:KPCR_EXCEPTION_LIST // + 0x2C
74
75 /* Set the exception handler chain terminator */
76 movl $0xffffffff, %fs:KPCR_EXCEPTION_LIST
77
78 /* Get a pointer to the current thread */
79 movl %fs:KPCR_CURRENT_THREAD, %esi
80
81 /* Save the old previous mode */
82 pushl %ss:KTHREAD_PREVIOUS_MODE(%esi) // + 0x30
83
84 /* Set the new previous mode based on the saved CS selector */
85 movl 0x24(%esp), %ebx
86 andl $1, %ebx
87 movb %bl, %ss:KTHREAD_PREVIOUS_MODE(%esi)
88
89 /* Save other registers */
90 pushl %eax // + 0x34
91 pushl %ecx // + 0x38
92 pushl %edx // + 0x3C
93 pushl %ds // + 0x40
94 pushl %es // + 0x44
95 pushl %gs // + 0x48
96 sub $0x28, %esp // + 0x70
97
98 #ifdef DBG
99 /* Trick gdb 6 into backtracing over the system call */
100 mov 0x6c(%esp), %ebx
101 pushl 4(%ebx) /* DebugEIP */ // + 0x74
102 #else
103 pushl 0x60(%esp) /* DebugEIP */ // + 0x74
104 #endif
105 pushl %ebp /* DebugEBP */ // + 0x78
106
107 /* Load the segment registers */
108 sti
109 movw $KERNEL_DS, %bx
110 movw %bx, %ds
111 movw %bx, %es
112
113 /* Save the old trap frame pointer where EDX would be saved */
114 movl KTHREAD_TRAP_FRAME(%esi), %ebx
115 movl %ebx, KTRAP_FRAME_EDX(%esp)
116
117 /* Allocate new Kernel stack frame */
118 movl %esp,%ebp
119
120 /* Save a pointer to the trap frame in the TCB */
121 movl %ebp, KTHREAD_TRAP_FRAME(%esi)
122
123 CheckValidCall:
124
125 #ifdef DBG
126 /*
127 * GDB thinks the function starts here and
128 * wants a standard prolog, so let's give it
129 */
130 pushl %ebp
131 movl %esp,%ebp
132 popl %ebp
133 #endif
134
135 /*
136 * Find out which table offset to use. Converts 0x1124 into 0x10.
137 * The offset is related to the Table Index as such: Offset = TableIndex x 10
138 */
139 movl %eax, %edi
140 shrl $8, %edi
141 andl $0x10, %edi
142 movl %edi, %ecx
143
144 /* Now add the thread's base system table to the offset */
145 addl KTHREAD_SERVICE_TABLE(%esi), %edi
146
147 /* Get the true syscall ID and check it */
148 movl %eax, %ebx
149 andl $0x0FFF, %eax
150 cmpl 8(%edi), %eax
151
152 /* Invalid ID, try to load Win32K Table */
153 jnb KiBBTUnexpectedRange
154
155 /* Users's current stack frame pointer is source */
156 movl %edx, %esi
157
158 /* Allocate room for argument list from kernel stack */
159 movl 12(%edi), %ecx
160 movb (%ecx, %eax), %cl
161 movzx %cl, %ecx
162
163 /* Allocate space on our stack */
164 subl %ecx, %esp
165
166 /* Get pointer to function */
167 movl (%edi), %edi
168 movl (%edi, %eax, 4), %eax
169
170 /* Copy the arguments from the user stack to our stack */
171 shr $2, %ecx
172 movl %esp, %edi
173 cld
174 rep movsd
175
176 /* Do the System Call */
177 call *%eax
178 movl %eax, KTRAP_FRAME_EAX(%ebp)
179
180 /* Deallocate the kernel stack frame */
181 movl %ebp, %esp
182
183 KeReturnFromSystemCall:
184
185 /* Get the Current Thread */
186 movl %fs:KPCR_CURRENT_THREAD, %esi
187
188 /* Restore the old trap frame pointer */
189 movl KTRAP_FRAME_EDX(%esp), %ebx
190 movl %ebx, KTHREAD_TRAP_FRAME(%esi)
191
192 _KiServiceExit:
193
194 /* Get the Current Thread */
195 cli
196 movl %fs:KPCR_CURRENT_THREAD, %esi
197
198 /* Deliver APCs only if we were called from user mode */
199 testb $1, KTRAP_FRAME_CS(%esp)
200 je KiRosTrapReturn
201
202 /* And only if any are actually pending */
203 cmpb $0, KTHREAD_PENDING_USER_APC(%esi)
204 je KiRosTrapReturn
205
206 /* Save pointer to Trap Frame */
207 movl %esp, %ebx
208
209 /* Raise IRQL to APC_LEVEL */
210 movl $1, %ecx
211 call @KfRaiseIrql@4
212
213 /* Save old IRQL */
214 pushl %eax
215
216 /* Deliver APCs */
217 sti
218 pushl %ebx
219 pushl $0
220 pushl $UserMode
221 call _KiDeliverApc@12
222 cli
223
224 /* Return to old IRQL */
225 popl %ecx
226 call @KfLowerIrql@4
227
228 KiRosTrapReturn:
229
230 /* Skip debug information and unsaved registers */
231 addl $0x30, %esp // + 0x48
232 popl %gs // + 0x44
233 popl %es // + 0x40
234 popl %ds // + 0x3C
235 popl %edx // + 0x38
236 popl %ecx // + 0x34
237 popl %eax // + 0x30
238
239 /* Restore the old previous mode */
240 popl %ebx // + 0x2C
241 movb %bl, %ss:KTHREAD_PREVIOUS_MODE(%esi)
242
243 /* Restore the old exception handler list */
244 popl %fs:KPCR_EXCEPTION_LIST // + 0x28
245
246 /* Restore final registers from trap frame */
247 popl %fs // + 0x24
248 popl %edi // + 0x20
249 popl %esi // + 0x1C
250 popl %ebx // + 0x18
251 popl %ebp // + 0x14
252 add $4, %esp // + 0x10
253
254 /* Check if previous CS is from user-mode */
255 testl $1, 4(%esp)
256
257 /* It is, so use Fast Exit */
258 jnz FastRet
259
260 /*
261 * Restore what the stub pushed, and return back to it.
262 * Note that we were CALLed, so the first thing on our stack is the ret EIP!
263 */
264 pop %edx // + 0x0C
265 pop %ecx // + 0x08
266 popf // + 0x04
267 jmp *%edx
268
269 IntRet:
270
271 iret
272
273 FastRet:
274
275 /* Is SYSEXIT Supported/Wanted? */
276 cmpl $0, %ss:_KiFastSystemCallDisable
277 jnz IntRet
278
279 /* Restore FS to TIB */
280 mov $TEB_SELECTOR, %ecx
281 mov %ecx, %fs
282
283 /* We will be cleaning up the stack ourselves */
284 popl %edx /* New Ring 3 EIP */
285 add $0x4, %esp /* Skip Ring 3 DS */
286 andl $~X86_EFLAGS_IF, (%esp) /* Remove IRQ hack from EFLAGS */
287 popfl /* Restore old EFLAGS */
288 popl %ecx /* Old Ring 3 SS:ESP */
289
290 /*
291 * At this point:
292 * ECX points to the old User Stack.
293 * EDX points to the instruction to execute in usermode after the sysenter
294 */
295 sti
296 sysexit
297
298 KiBBTUnexpectedRange:
299
300 /* If this isn't a Win32K call, fail */
301 cmp $0x10, %ecx
302 jne InvalidCall
303
304 /* Set up Win32K Table */
305 pushl %edx
306 pushl %ebx
307 call _KiServiceCheck
308 popl %eax
309 popl %edx
310
311 /* Try the Call again */
312 jmp CheckValidCall
313
314 InvalidCall:
315
316 /* Invalid System Call */
317 movl $0xC000001C, %eax /* STATUS_INVALID_SYSTEM_SERVICE */
318 movl %eax, KTRAP_FRAME_EAX(%ebp)
319 jmp _KiServiceExit
320
321 _KiServiceExit2:
322
323 /* Get the Current Thread */
324 cli
325 movl %fs:KPCR_CURRENT_THREAD, %esi
326
327 /* Deliver APCs only if we were called from user mode */
328 testb $1, KTRAP_FRAME_CS(%esp)
329 je KiRosTrapReturn
330
331 /* And only if any are actually pending */
332 cmpb $0, KTHREAD_PENDING_USER_APC(%esi)
333 je KiRosTrapReturn
334
335 /* Save pointer to Trap Frame */
336 movl %esp, %ebx
337
338 /* Raise IRQL to APC_LEVEL */
339 movl $1, %ecx
340 call @KfRaiseIrql@4
341
342 /* Save old IRQL */
343 pushl %eax
344
345 /* Deliver APCs */
346 sti
347 pushl %ebx
348 pushl $0
349 pushl $UserMode
350 call _KiDeliverApc@12
351 cli
352
353 /* Return to old IRQL */
354 popl %ecx
355 call @KfLowerIrql@4
356
357 /* Skip useless Debug Data */
358 addl $0x18, %esp // + 0x74
359
360 /* Restore Debug Registers */
361 popl %eax // + 0x5C
362 movl %eax, %dr0
363 popl %eax // + 0x58
364 movl %eax, %dr1
365 popl %eax // + 0x54
366 movl %eax, %dr2
367 popl %eax // + 0x50
368 movl %eax, %dr3
369 popl %eax // + 0x4C
370 movl %eax, %dr6
371 popl %eax // + 0x48
372 movl %eax, %dr7
373
374 /* Restore Registers */
375 popl %gs // + 0x44
376 popl %es // + 0x40
377 popl %ds // + 0x3C
378 popl %edx // + 0x38
379 popl %ecx // + 0x34
380 popl %eax // + 0x30
381
382 /* Restore the old previous mode */
383 popl %ebx // + 0x2C
384 movb %bl, %ss:KTHREAD_PREVIOUS_MODE(%esi)
385
386 /* Restore the old exception handler list */
387 popl %fs:KPCR_EXCEPTION_LIST // + 0x28
388
389 /* Restore final registers from trap frame */
390 popl %fs // + 0x24
391 popl %edi // + 0x20
392 popl %esi // + 0x1C
393 popl %ebx // + 0x18
394 popl %ebp // + 0x14
395 add $4, %esp // + 0x10
396
397 /* Return to user-mode */
398 iret
399
400 .intel_syntax noprefix
401 _KiDebugService:
402
403 /* Create the Trap Frame */
404 push 0
405 push ebp
406 push ebx
407 push esi
408 push edi
409 push fs
410
411 /* Switch to correct FS */
412 mov bx, PCR_SELECTOR
413 mov fs, bx
414
415 /* Save Exception List */
416 push fs:[KPCR_EXCEPTION_LIST]
417
418 /* Use Old Previous Mode */
419 mov ebx, fs:[KPCR_CURRENT_THREAD]
420 push [ebx+KTHREAD_PREVIOUS_MODE]
421
422 /* Continue building the Trap Frame */
423 push eax
424 push ecx
425 push edx
426 push ds
427 push es
428 push gs
429
430 /* Switch Segments to Kernel */
431 mov bx, KERNEL_DS
432 mov ds, bx
433 mov es, bx
434
435 /* Save Debug Registers */
436 mov ebx, eax
437 mov eax, dr7
438 push eax
439 mov eax, dr6
440 push eax
441 mov eax, dr3
442 push eax
443 mov eax, dr2
444 push eax
445 mov eax, dr1
446 push eax
447 mov eax, dr0
448 push eax
449 mov eax, ebx
450
451 /* Skip useless debug data */
452 sub esp, 0x18
453
454 /* Call debug service dispatcher */
455 push edx
456 push ecx
457 push eax
458 call _KdpServiceDispatcher@12
459
460 /* Exit through common routine */
461 jmp _KiServiceExit2