2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/i386/ctxswitch.S
5 * PURPOSE: Thread Context Switching
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
10 /* INCLUDES ******************************************************************/
13 #include <internal/i386/segment.h>
14 #include <internal/i386/ke.h>
15 #include <internal/i386/fpu.h>
16 #include <internal/ps.h>
18 #include <internal/ntoskrnl.h>
19 .intel_syntax noprefix
22 #define SIZEOF_TRAP_FRAME 0x8c
25 /* GLOBALS ****************************************************************/
27 /* FUNCTIONS ****************************************************************/
32 * The KiThreadStartup routine is the beginning of any thread.
35 * SystemRoutine - Pointer to the System Startup Routine. Either
36 * PspUserThreadStartup or PspSystemThreadStartup
38 * StartRoutine - For Kernel Threads only, specifies the starting execution
39 * point of the new thread.
41 * StartContext - For Kernel Threads only, specifies a pointer to variable
42 * context data to be sent to the StartRoutine above.
44 * UserThread - Indicates whether or not this is a user thread. This tells
45 * us if the thread has a context or not.
47 * TrapFrame - Pointer to the KTHREAD to which the caller wishes to
51 * Should never return for a system thread. Returns through the System Call
52 * Exit Dispatcher for a user thread.
55 * If a return from a system thread is detected, a bug check will occur.
58 .globl _KiThreadStartup@156
62 * Clear all the non-volatile registers, so the thread won't be tempted to
63 * expect any static data (like some badly coded usermode/win9x apps do)
70 /* It's now safe to go to APC */
75 * Call the System Routine which is right on our stack now.
76 * After we pop the pointer, the Start Routine/Context will be on the
77 * stack, as parameters to the System Routine
82 /* The thread returned... was it a user-thread? */
87 /* Yes it was, set our trapframe for the System Call Exit Dispatcher */
90 /* Exit back to user-mode */
95 /* A system thread returned...this is very bad! */
99 * KiSwapContextInternal
101 * The KiSwapContextInternal routine switches context to another thread.
104 * ESI - Pointer to the KTHREAD to which the caller wishes to
106 * EDI - Pointer to the KTHREAD to which the caller wishes to
113 * Absolutely all registers except ESP can be trampled here for maximum code flexibility.
116 .globl @KiSwapContextInternal@0
117 @KiSwapContextInternal@0:
119 //jmp SaveTrapFrameForKDB
120 SaveTrapFrameForKDB_Return:
123 /* Get the PCR. It's faster to use ebx+offset then fs:offset */
124 mov ebx, [fs:KPCR_SELF]
126 /* Set the Thread to running */
127 mov byte ptr [esi+KTHREAD_STATE], Running
129 /* Save the Exception list */
130 push [ebx+KPCR_EXCEPTION_LIST]
132 /* Switching, disable interrupts now */
136 /* Save FPU state if the thread has used it. */
137 mov dword ptr [ebx+KPCR_NPX_THREAD], 0
138 test byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_DIRTY
140 mov eax, [edi+KTHREAD_INITIAL_STACK]
141 cmp dword ptr _FxsrSupport, 0
143 fxsave [eax-SIZEOF_FX_SAVE_AREA]
146 fnsave [eax-SIZEOF_FX_SAVE_AREA]
148 mov byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_VALID
150 #endif /* CONFIG_SMP */
152 /* Save the stack pointer in this processors TSS */
153 mov ebp, [ebx+KPCR_TSS]
154 push ss:[ebp+KTSS_ESP0]
157 mov [edi+KTHREAD_KERNEL_STACK], esp
158 mov esp, [esi+KTHREAD_KERNEL_STACK]
160 /* Stack is OK, safe to enable interrupts now */
163 /* Check if address space switch is needed */
164 mov eax, [edi+KTHREAD_APCSTATE_PROCESS]
165 cmp eax, [esi+KTHREAD_APCSTATE_PROCESS]
167 /* If they match, then use the fast-path and skip all this */
170 /* Get the new Process. */
171 mov edi, [esi+KTHREAD_APCSTATE_PROCESS]
173 /* Check if we need an LDT */
175 cmp [edi+KPROCESS_LDT_DESCRIPTOR0], eax
178 /* Write the LDT Selector */
179 mov ebp, [ebx+KPCR_GDT]
180 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR0]
181 mov [ebp+LDT_SELECTOR], eax
182 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR1]
183 mov [ebp+LDT_SELECTOR+4], eax
185 /* Save LDT Selector */
186 mov eax, LDT_SELECTOR
194 mov ecx, [edi+KPROCESS_IOPM_OFFSET]
196 /* Set current IOPM offset in the TSS */
197 mov [ebp+KTSS_IOMAPBASE], cx
199 /* Change the address space */
200 mov eax, [edi+KPROCESS_DIRECTORY_TABLE_BASE]
206 mov eax, [esi+KTHREAD_TEB]
207 mov ecx, [ebx+KPCR_GDT]
213 /* Increase context switches */
214 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
216 /* Set TS in cr0 to catch FPU code and load the FPU state when needed */
218 cmp [ebx+KPCR_NPX_THREAD], esi
220 #endif /* !CONFIG_SMP */
226 /* Restore the stack pointer in this processors TSS */
227 pop ss:[ebp+KTSS_ESP0]
229 /* Restore exception list */
230 pop [ebx+KPCR_EXCEPTION_LIST]
233 call @KeReleaseDispatcherDatabaseLockFromDpcLevel@0
239 * The KiSwapContext routine switches context to another thread.
242 * TargetThread - Pointer to the KTHREAD to which the caller wishes to
246 * The WaitStatus of the Target Thread. NOT YET SUPPORTED.
249 * This is a wrapper around KiSwapContextInternal which will save all the
250 * non-volatile registers so that the Internal function can use all of
251 * them. It will also save the old current thread and set the new one.
253 * The calling thread does not return after KiSwapContextInternal until
254 * another thread switches to IT.
257 .globl @KiSwapContext@4
259 /* Note, we CANNOT touch ebp */
261 /* Save 4 registers */
264 /* Save all the non-volatile ones */
270 /* Get the Current Thread */
271 mov edi, fs:[KPCR_CURRENT_THREAD]
273 /* Get the New Thread */
276 /* Save it as Current thread */
277 mov fs:[KPCR_CURRENT_THREAD], esi
279 /* Do the swap with the registers correctly setup */
280 call @KiSwapContextInternal@0
282 /* Return the registers */
295 /* Set up a trap frame */
297 /* Fake Interrupt Stack */
301 push [esp+12] /* EIP */ // 0x68
302 mov [esp+16], ss // 0x78
305 push 0 /* Error Code */ // 0x64
311 push -1 /* Exception List */ // 0x4C
312 push 0 /* Previous Mode */ // 0x48
319 sub esp, 0x28 /* Debug Registers */ // 0x8
320 push [esp+60] /* Debug EIP */ // 0x4
321 push ebp /* Debug EBP */ // 0x0
326 /* Push old Trap Frame */
327 push [edi+KTHREAD_TRAP_FRAME]
330 mov [edi+KTHREAD_TRAP_FRAME], ebp
333 push offset RestoreTrapFrameForKDB
336 mov ebp, [ebp+KTRAP_FRAME_EBP]
338 /* Jump to normal code */
339 jmp SaveTrapFrameForKDB_Return
341 RestoreTrapFrameForKDB:
343 /* Restore the old trapframe */
344 pop [edi+KTHREAD_TRAP_FRAME]
346 /* Pop unused portions of the trap frame */
349 /* Restore registers from Trap frame */
362 /* Remove SS:ESP from the stack */
370 /* Restore Fake INT Stack */
374 /* Return to the caller. */