2 * FILE: ntoskrnl/ke/i386/syscall.S
3 * COPYRIGHT: See COPYING in the top level directory
4 * PURPOSE: System Call Handler
5 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
9 #include <internal/i386/ke.h>
11 #include <ndk/i386/segment.h>
14 #define STATUS_INVALID_SYSTEM_SERVICE 0xC000001C
17 .globl _KiServiceExit2
18 .globl _KiFastCallEntry
19 .globl _KiSystemService
20 .globl _KiDebugService
21 .intel_syntax noprefix
24 * NOTE: I will create some macros for trap entry and exit,
25 * DR* register restoration, modified frame exit, etc, if GAS
26 * allows it/I find a way how. This would remove a lot of
27 * duplicated code in this file plus the other irq/trap asm files.
28 * I think this is similar to what NT does, if you look at teh
29 * Dr_kit*_a functions which look auto-generated.
33 * There are 3 main types of Trap Exits:
36 * - Clear interrupt flag
37 * - Common User APC Dispatching
38 * - Common exit code; segments and volatiles are not restored
39 * You use this for System Call return, when volatiles are irrelevant.
40 * (NtContinue, NtRaiseException, KiCallUserMode and all System Call returns)
43 * - Clear interrupt flag
44 * - Common User APC Dispatching
45 * - Common exit code; the entire frame is restored.
46 * You use this when volatiles and other registers need to be restored.
47 * For example, if debugging is active (NtContinue, NtRaiseException).
50 * - Clear interrupt flag
51 * - Common User APC Dispatching
52 * - Common exit code; the entire frame is restored but *NOT* the Previous Mode
53 * You use this in the same context as KiServiceExit2, but when the Previous Mode
54 * should be tampered with. Clearly, as its name suggests, this routine is mostly
55 * useful for End Of Interrupts.
56 * Note that this routine is EXPORTED.
57 * Note that this routine must called by a JMP, not a CALL.
61 * The common exit code has 3 modes of operation:
62 * - Whether or not to restore segments
63 * - Whether or not to restore volatiles
64 * - Whether or not to restore the previous mode
65 * All these are exemplified by the 3 trap exits shown above
69 * There is also common Debug Code present in the common exit code, which
70 * in turn calls common code to save the debug registers
75 * - Dig in trap code and see why we need to push/pop the segments,
76 * which -shouldn't- be needed on syscalls; one of the things
77 * missing for this to work is lazy loading in the GPF handler,
78 * but there are other things to consider.
79 * - Use macros and merge with trap.s nicely
83 * Entries will be discussed later.
86 /*** This file is a mess; it is being worked on. Please contact Alex:
87 *** alex@relsoft.net if you want to make any changes to it before this
91 /* FUNCTIONS ***************************************************************/
95 /* Restore ESP0 stack */
97 mov ecx, [fs:KPCR_TSS]
98 mov esp, ss:[ecx+KTSS_ESP0]
100 /* FIXME: Generate Trap 6*/
105 // ==================== UNIQUE SYSENTER STUB. DO NOT DUPLICATE ============//
107 mov ecx, PCR_SELECTOR
110 /* Set DS/ES to Kernel Selector */
115 /* Set the current stack to Kernel Stack */
116 mov ecx, [fs:KPCR_TSS]
117 mov esp, ss:[ecx+KTSS_ESP0]
119 /* Set up a fake INT Stack. */
121 push edx /* Ring 3 SS:ESP */
122 pushf /* Ring 3 EFLAGS */
123 push 2 /* Ring 0 EFLAGS */
124 add edx, 8 /* Skip user parameter list */
125 popf /* Set our EFLAGS */
126 or dword ptr [esp], X86_EFLAGS_IF /* Re-enable IRQs in EFLAGS, to fake INT */
128 push KUSER_SHARED_SYSCALL_RET
130 /* Setup the Trap Frame stack */
138 /* Save pointer to our PCR */
139 mov ebx, [fs:KPCR_SELF]
141 /* Get a pointer to the current thread */
142 mov esi, [ebx+KPCR_CURRENT_THREAD]
144 /* Set the exception handler chain terminator */
145 push [ebx+KPCR_EXCEPTION_LIST]
146 mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
148 /* Use the thread's stack */
149 mov ebp, [esi+KTHREAD_INITIAL_STACK]
151 /* Push previous mode */
155 /* Save other registers */
156 sub $0xC, %esp // + 0x70
157 pushl $USER_DS // + 0x40
158 pushl $USER_DS // + 0x44
160 sub $0x30, %esp // + 0x70
161 .intel_syntax noprefix
163 /* Make space for us on the stack */
166 /* Write the previous mode */
167 mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], UserMode
174 and dword ptr [ebp+KTRAP_FRAME_DR7], 0
176 /* Check if the thread was being debugged */
177 test byte ptr [esi+KTHREAD_DEBUG_ACTIVE], 0xFF
179 /* Jump to shared code or DR Save */
180 //jnz Dr_FastCallDrSave
185 // ==================== UNIQUE SYSCALL TRAP ENTRY DO NOT DUPLICATE ============//
186 /* Create a trap frame */
194 /* Load PCR Selector into fs */
195 mov ebx, PCR_SELECTOR
198 /* Get a pointer to the current thread */
199 mov esi, [fs:KPCR_CURRENT_THREAD]
201 /* Save the previous exception list */
202 push [fs:KPCR_EXCEPTION_LIST]
204 /* Set the exception handler chain terminator */
205 mov dword ptr [fs:KPCR_EXCEPTION_LIST], -1
207 /* Save the old previous mode */
208 push ss:[esi+KTHREAD_PREVIOUS_MODE]
211 /* Save other registers */
212 sub $0xC, %esp // + 0x70
216 sub $0x30, %esp // + 0x70
217 .intel_syntax noprefix
219 /* Set the new previous mode based on the saved CS selector */
222 mov byte ptr ss:[esi+KTHREAD_PREVIOUS_MODE], bl
224 /* Go on the Kernel stack frame */
227 /* Save the old trap frame pointer where EDX would be saved */
228 mov ebx, [esi+KTHREAD_TRAP_FRAME]
229 mov [ebp+KTRAP_FRAME_EDX], ebx
231 // ==================== COMMON DR SAVE CHECK.AND DEBUG FRAME SETUP ============//
233 and dword ptr [ebp+KTRAP_FRAME_DR7], 0
235 /* Check if the thread was being debugged */
236 test byte ptr [esi+KTHREAD_DEBUG_ACTIVE], 0xFF
240 /* Save a pointer to the trap frame in the TCB */
242 mov [esi+KTHREAD_TRAP_FRAME], ebp
244 /* Get the Debug Trap Frame EBP/EIP */
245 mov ebx, [ebp+KTRAP_FRAME_EBP]
246 mov edi, [ebp+KTRAP_FRAME_EIP]
248 /* Write the debug data */
249 mov [ebp+KTRAP_FRAME_DEBUGPOINTER], edx
250 mov dword ptr [ebp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
251 mov [ebp+KTRAP_FRAME_DEBUGEBP], ebx
252 mov [ebp+KTRAP_FRAME_DEBUGEIP], edi
254 // ============= END OF COMMON DR SAVE CHECK.AND DEBUG FRAME SETUP ============//
255 /* Enable interrupts */
261 * Find out which table offset to use. Converts 0x1124 into 0x10.
262 * The offset is related to the Table Index as such: Offset = TableIndex x 10
269 /* Now add the thread's base system table to the offset */
270 add edi, [esi+KTHREAD_SERVICE_TABLE]
272 /* Get the true syscall ID and check it */
277 /* Invalid ID, try to load Win32K Table */
278 jnb KiBBTUnexpectedRange
280 #if 0 // <== Disabled for two reasons: We don't save TEB in 0x18, but KPCR.
281 // <== We don't have a KeGdiFlushUserBatch callback yet (needs to be
282 // sent through the PsInitializeWin32Callouts structure)
283 /* Check if this was Win32K */
288 mov ecx, [fs:KPCR_TEB]
290 /* Check if we should flush the User Batch */
292 or ebx, [ecx+TEB_GDI_BATCH_COUNT]
298 call [_KeGdiFlushUserBatch]
304 /* Users's current stack frame pointer is source */
307 /* Allocate room for argument list from kernel stack */
312 /* Get pointer to function */
316 /* Allocate space on our stack */
320 * Copy the arguments from the user stack to our stack
321 * FIXME: This needs to be probed with MmSystemRangeStart
327 /* Do the System Call */
330 /* Deallocate the kernel stack frame */
333 KeReturnFromSystemCall:
335 /* Get the Current Thread */
336 mov ecx, [fs:KPCR_CURRENT_THREAD]
338 /* Restore the old trap frame pointer */
339 mov edx, [ebp+KTRAP_FRAME_EDX]
340 mov [ecx+KTHREAD_TRAP_FRAME], edx
343 /* Disable interrupts */
346 // ================= COMMON USER-MODE APC DELIVERY CHECK ============//
347 /* Check for V86 mode */
348 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
351 /* Deliver APCs only if we were called from user mode */
352 test byte ptr [ebp+KTRAP_FRAME_CS], 1
355 /* Get the current thread */
357 mov ebx, [fs:KPCR_CURRENT_THREAD]
359 /* Make it non-alerted */
360 mov byte ptr [ebx+KTHREAD_ALERTED], 0
362 /* And only if any are actually pending */
363 cmp byte ptr [ebx+KTHREAD_PENDING_USER_APC], 0
366 /* Save pointer to Trap Frame */
369 // ================= PRESENT ONLY IF VOLATILES NEEDED ============//
370 /* Save some stuff that raising IRQL will kill */
371 mov [ebx+KTRAP_FRAME_EAX], eax
372 mov dword ptr [ebx+KTRAP_FRAME_FS], TEB_SELECTOR
373 mov dword ptr [ebx+KTRAP_FRAME_DS], USER_DS
374 mov dword ptr [ebx+KTRAP_FRAME_ES], USER_DS
375 mov dword ptr [ebx+KTRAP_FRAME_GS], 0
376 // ============= END PRESENT ONLY IF VOLATILES NEEDED ============//
378 /* Raise IRQL to APC_LEVEL */
390 call _KiDeliverApc@12
392 /* Return to old IRQL */
396 /* Restore EAX (only in volatile case) */
397 mov eax, [ebx+KTRAP_FRAME_EAX]
400 // ============== END COMMON USER-MODE APC DELIVERY CHECK ============//
403 // ========================= COMMON TRAP EXIT CODE ===================//
404 /* Restore exception list */
405 mov edx, [esp+KTRAP_FRAME_EXCEPTION_LIST]
406 mov [fs:KPCR_EXCEPTION_LIST], edx
408 // ==================== ONLY IF PREVIOUS MODE NEEDED ==================//
409 /* Restore previous mode */
410 mov ecx, [esp+KTRAP_FRAME_PREVIOUS_MODE]
411 mov esi, [fs:KPCR_CURRENT_THREAD]
412 mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], cl
413 // ==================== END IF PREVIOUS MODE NEEDED ===================//
416 test dword ptr [esp+KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
419 /* Check if the frame was edited */
420 test word ptr [esp+KTRAP_FRAME_CS], FRAME_EDITED
423 // ==================== ONLY IF FULL RESTORE NEEDED ===================//
424 /* Check the old mode */
425 cmp word ptr [esp+KTRAP_FRAME_CS], USER_CS
426 bt word ptr [esp+KTRAP_FRAME_CS], 0
429 // ==================== END IF FULL RESTORE NEEDED ====================//
431 /* Skip debug information and unsaved registers */
442 //lea esp, [ebp+KTRAP_FRAME_FS] <= BUG IN WIN32K CALLBACKS! STACK GETS SMASHED
446 /* Skip debug information and unsaved registers */
447 //lea esp, [ebp+KTRAP_FRAME_EDI] <= BUG IN WIN32K CALLBACKS! STACK GETS SMASHED
453 /* Check for ABIOS */
454 cmp word ptr [esp+8], 0x80
461 /* Check if previous CS is from user-mode */
462 test dword ptr [esp+4], 1
464 /* It is, so use Fast Exit */
467 /* Jump back to stub */
478 /* Is SYSEXIT Supported/Wanted? */
479 cmp dword ptr ss:[_KiFastSystemCallDisable], 0
481 test dword ptr [esp+8], X86_EFLAGS_TF
484 /* Restore FS to TIB */
485 mov ecx, TEB_SELECTOR
488 /* We will be cleaning up the stack ourselves */
489 pop edx /* New Ring 3 EIP */
490 add esp, 4 /* Skip Ring 3 DS */
491 and dword ptr [esp], ~X86_EFLAGS_IF /* Remove IRQ hack from EFLAGS */
492 popf /* Restore old EFLAGS */
493 pop ecx /* Old Ring 3 SS:ESP */
497 * ECX points to the old User Stack.
498 * EDX points to the instruction to execute in usermode after the sysenter
505 /* Not yet supported */
510 mov eax, [esp+KTRAP_FRAME_EAX]
515 /* Restore segments and volatiles */
522 /* Jump back to mainline code */
526 /* Restore real CS value */
527 mov ebx, [esp+KTRAP_FRAME_TEMPCS]
528 mov [esp+KTRAP_FRAME_CS], ebx
531 * If ESP was modified, then a special interrupt exit stack
532 * must be created to "update" ESP's value in a legal manner
534 mov ebx, [esp+KTRAP_FRAME_TEMPESP]
536 mov [esp+KTRAP_FRAME_ERROR_CODE], ebx
538 /* Copy Interrupt Stack */
539 mov esi, [esp+KTRAP_FRAME_EFLAGS]
541 mov esi, [esp+KTRAP_FRAME_CS]
543 mov esi, [esp+KTRAP_FRAME_EIP]
547 add esp, KTRAP_FRAME_EDI
555 KiBBTUnexpectedRange:
557 /* If this isn't a Win32K call, fail */
561 /* Set up Win32K Table */
566 /* FIXME: Handle failure */
570 /* Try the Call again */
575 /* Invalid System Call */
576 mov eax, STATUS_INVALID_SYSTEM_SERVICE
577 jmp KeReturnFromSystemCall
581 /* Disable interrupts */
584 /* Check for V86 mode */
585 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
588 /* Deliver APCs only if we were called from user mode */
589 test byte ptr [ebp+KTRAP_FRAME_CS], 1
592 /* Get the current thread */
594 mov ebx, [fs:KPCR_CURRENT_THREAD]
596 /* Make it non-alerted */
597 mov byte ptr [ebx+KTHREAD_ALERTED], 0
599 /* And only if any are actually pending */
600 cmp byte ptr [ebx+KTHREAD_PENDING_USER_APC], 0
603 /* Save pointer to Trap Frame */
606 /* Raise IRQL to APC_LEVEL */
618 call _KiDeliverApc@12
620 /* Return to old IRQL */
628 /* Restore exception list */
629 mov edx, [esp+KTRAP_FRAME_EXCEPTION_LIST]
630 mov [fs:KPCR_EXCEPTION_LIST], edx
632 /* Restore previous mode */
633 mov ecx, [esp+KTRAP_FRAME_PREVIOUS_MODE]
634 mov esi, [fs:KPCR_CURRENT_THREAD]
635 mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], cl
638 test dword ptr [esp+KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
641 /* Check if the frame was edited */
642 test word ptr [esp+KTRAP_FRAME_CS], FRAME_EDITED
645 /* Restore volatiles */
646 mov edx, [esp+KTRAP_FRAME_EDX]
647 mov ecx, [esp+KTRAP_FRAME_ECX]
648 mov eax, [esp+KTRAP_FRAME_EAX]
650 /* Check if it was kernel */
651 cmp word ptr [ebp+KTRAP_FRAME_CS], KERNEL_CS
655 lea esp, [ebp+KTRAP_FRAME_GS]
657 /* Restore segments and volatiles */
661 lea esp, [ebp+KTRAP_FRAME_FS]
665 /* Skip debug information and unsaved registers */
666 lea esp, [ebp+KTRAP_FRAME_EDI]
672 /* Check for ABIOS */
673 cmp word ptr [esp+8], 0x80
676 /* Pop error code and return */
681 /* Restore real CS value */
682 mov ebx, [esp+KTRAP_FRAME_TEMPCS]
683 mov [esp+KTRAP_FRAME_CS], ebx
686 * If ESP was modified, then a special interrupt exit stack
687 * must be created to "update" ESP's value in a legal manner
689 mov ebx, [esp+KTRAP_FRAME_TEMPESP]
691 mov [esp+KTRAP_FRAME_ERROR_CODE], ebx
693 /* Copy Interrupt Stack */
694 mov esi, [esp+KTRAP_FRAME_EFLAGS]
696 mov esi, [esp+KTRAP_FRAME_CS]
698 mov esi, [esp+KTRAP_FRAME_EIP]
701 /* Restore volatiles */
702 mov eax, [esp+KTRAP_FRAME_EAX]
703 mov edx, [esp+KTRAP_FRAME_EDX]
704 mov ecx, [esp+KTRAP_FRAME_ECX]
707 add esp, KTRAP_FRAME_EDI
717 /* Create the Trap Frame */
725 /* Switch to correct FS */
729 /* Save Exception List */
730 push fs:[KPCR_EXCEPTION_LIST]
732 /* Traps don't need the previous mode */
735 /* Continue building the Trap Frame */
744 /* Switch Segments to Kernel */
752 /* Check if this was from V86 Mode */
753 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
756 /* Get current thread */
757 mov ecx, [fs:KPCR_CURRENT_THREAD]
761 and dword ptr [ebp+KTRAP_FRAME_DR7], 0
763 /* Check if the thread was being debugged */
764 test byte ptr [ecx+KTHREAD_DEBUG_ACTIVE], 0xFF
767 /* Get the Debug Trap Frame EBP/EIP */
768 mov ebx, [ebp+KTRAP_FRAME_EBP]
769 mov edi, [ebp+KTRAP_FRAME_EIP]
771 /* Write the debug data */
772 mov [ebp+KTRAP_FRAME_DEBUGPOINTER], edx
773 mov dword ptr [ebp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
774 mov [ebp+KTRAP_FRAME_DEBUGEBP], ebx
775 mov [ebp+KTRAP_FRAME_DEBUGEIP], edi
777 /* Call debug service dispatcher */
778 push [ebp+KTRAP_FRAME_EDX]
779 push [ebp+KTRAP_FRAME_ECX]
780 push [ebp+KTRAP_FRAME_EAX]
781 call _KdpServiceDispatcher@12
783 /* Exit through common routine */
784 jmp Kei386EoiHelper@0
788 /* Disable interrupts */
791 /* Check for V86 mode */
792 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
795 /* Deliver APCs only if we were called from user mode */
796 test byte ptr [ebp+KTRAP_FRAME_CS], 1
799 /* Get the current thread */
801 mov ebx, [fs:KPCR_CURRENT_THREAD]
803 /* Make it non-alerted */
804 mov byte ptr [ebx+KTHREAD_ALERTED], 0
806 /* And only if any are actually pending */
807 cmp byte ptr [ebx+KTHREAD_PENDING_USER_APC], 0
810 /* Save pointer to Trap Frame */
813 /* Raise IRQL to APC_LEVEL */
825 call _KiDeliverApc@12
827 /* Return to old IRQL */
835 /* Restore exception list */
836 mov edx, [esp+KTRAP_FRAME_EXCEPTION_LIST]
837 mov [fs:KPCR_EXCEPTION_LIST], edx
840 test dword ptr [esp+KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
843 /* Check if the frame was edited */
844 test word ptr [esp+KTRAP_FRAME_CS], FRAME_EDITED
847 /* Restore volatiles */
848 mov edx, [esp+KTRAP_FRAME_EDX]
849 mov ecx, [esp+KTRAP_FRAME_ECX]
850 mov eax, [esp+KTRAP_FRAME_EAX]
852 /* Check if it was kernel */
853 cmp word ptr [ebp+KTRAP_FRAME_CS], KERNEL_CS
857 lea esp, [ebp+KTRAP_FRAME_GS]
859 /* Restore segments and volatiles */
863 lea esp, [ebp+KTRAP_FRAME_FS]
867 /* Skip debug information and unsaved registers */
868 lea esp, [ebp+KTRAP_FRAME_EDI]
874 /* Check for ABIOS */
875 cmp word ptr [esp+8], 0x80
878 /* Pop error code and return */
883 /* Restore real CS value */
884 mov ebx, [esp+KTRAP_FRAME_TEMPCS]
885 mov [esp+KTRAP_FRAME_CS], ebx
888 * If ESP was modified, then a special interrupt exit stack
889 * must be created to "update" ESP's value in a legal manner
891 mov ebx, [esp+KTRAP_FRAME_TEMPESP]
893 mov [esp+KTRAP_FRAME_ERROR_CODE], ebx
895 /* Copy Interrupt Stack */
896 mov esi, [esp+KTRAP_FRAME_EFLAGS]
898 mov esi, [esp+KTRAP_FRAME_CS]
900 mov esi, [esp+KTRAP_FRAME_EIP]
903 /* Restore volatiles */
904 mov eax, [esp+KTRAP_FRAME_EAX]
905 mov edx, [esp+KTRAP_FRAME_EDX]
906 mov ecx, [esp+KTRAP_FRAME_ECX]
909 add esp, KTRAP_FRAME_EDI
917 .globl _NtRaiseException@12
918 _NtRaiseException@12:
920 /* NOTE: We -must- be called by Zw* to have the right frame! */
921 /* Push the stack frame */
924 /* Get the current thread and restore its trap frame */
925 mov ebx, [fs:KPCR_CURRENT_THREAD]
926 mov edx, [ebp+KTRAP_FRAME_EDX]
927 mov [ebx+KTHREAD_TRAP_FRAME], edx
929 /* Set up stack frame */
932 /* Get the Trap Frame in EBX */
935 /* Get the exception list and restore */
936 mov eax, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
937 mov [fs:KPCR_EXCEPTION_LIST], eax
939 /* Get the parameters */
940 mov edx, [ebp+16] /* Search frames */
941 mov ecx, [ebp+12] /* Context */
942 mov eax, [ebp+8] /* Exception Record */
944 /* Raise the exception */
950 call _KiRaiseException@20
952 /* Restore trap frame in EBP */
956 /* Check the result */
960 /* Restore debug registers too */
966 /* NOTE: We -must- be called by Zw* to have the right frame! */
967 /* Push the stack frame */
970 /* Get the current thread and restore its trap frame */
971 mov ebx, [fs:KPCR_CURRENT_THREAD]
972 mov edx, [ebp+KTRAP_FRAME_EDX]
973 mov [ebx+KTHREAD_TRAP_FRAME], edx
975 /* Set up stack frame */
978 /* Save the parameters */
982 /* Call KiContinue */
988 /* Check if we failed (bad context record) */
992 /* Check if test alert was requested */
993 cmp dword ptr [ebp+12], 0
996 /* Test alert for the thread */
997 mov al, [ebx+KTHREAD_PREVIOUS_MODE]
999 call _KeTestAlertThread@4
1002 /* Return to previous context */