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/ke.h>
14 #include <internal/asm.h>
15 #include <ndk/i386/segment.h>
16 .intel_syntax noprefix
19 #define SIZEOF_TRAP_FRAME 0x8c
22 /* GLOBALS ****************************************************************/
24 /* FUNCTIONS ****************************************************************/
29 * The KiThreadStartup routine is the beginning of any thread.
32 * SystemRoutine - Pointer to the System Startup Routine. Either
33 * PspUserThreadStartup or PspSystemThreadStartup
35 * StartRoutine - For Kernel Threads only, specifies the starting execution
36 * point of the new thread.
38 * StartContext - For Kernel Threads only, specifies a pointer to variable
39 * context data to be sent to the StartRoutine above.
41 * UserThread - Indicates whether or not this is a user thread. This tells
42 * us if the thread has a context or not.
44 * TrapFrame - Pointer to the KTHREAD to which the caller wishes to
48 * Should never return for a system thread. Returns through the System Call
49 * Exit Dispatcher for a user thread.
52 * If a return from a system thread is detected, a bug check will occur.
55 .globl _KiThreadStartup@156
59 * Clear all the non-volatile registers, so the thread won't be tempted to
60 * expect any static data (like some badly coded usermode/win9x apps do)
67 /* It's now safe to go to APC */
72 * Call the System Routine which is right on our stack now.
73 * After we pop the pointer, the Start Routine/Context will be on the
74 * stack, as parameters to the System Routine
79 /* The thread returned... was it a user-thread? */
84 /* Yes it was, set our trapframe for the System Call Exit Dispatcher */
87 /* Exit back to user-mode */
92 /* A system thread returned...this is very bad! */
96 * KiSwapContextInternal
98 * The KiSwapContextInternal routine switches context to another thread.
101 * ESI - Pointer to the KTHREAD to which the caller wishes to
103 * EDI - Pointer to the KTHREAD to which the caller wishes to
110 * Absolutely all registers except ESP can be trampled here for maximum code flexibility.
113 .globl @KiSwapContextInternal@0
114 @KiSwapContextInternal@0:
116 jmp SaveTrapFrameForKDB
117 SaveTrapFrameForKDB_Return:
120 /* Get the PCR. It's faster to use ebx+offset then fs:offset */
121 mov ebx, [fs:KPCR_SELF]
123 /* Set the Thread to running */
124 mov byte ptr [esi+KTHREAD_STATE], Running
126 /* Save the Exception list */
127 push [ebx+KPCR_EXCEPTION_LIST]
129 /* Switching, disable interrupts now */
133 /* Save FPU state if the thread has used it. */
134 mov dword ptr [ebx+KPCR_NPX_THREAD], 0
135 test byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_DIRTY
137 mov eax, [edi+KTHREAD_INITIAL_STACK]
138 cmp dword ptr _FxsrSupport, 0
140 fxsave [eax-SIZEOF_FX_SAVE_AREA]
143 fnsave [eax-SIZEOF_FX_SAVE_AREA]
145 mov byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_VALID
147 #endif /* CONFIG_SMP */
149 /* Save the stack pointer in this processors TSS */
150 mov ebp, [ebx+KPCR_TSS]
151 push ss:[ebp+KTSS_ESP0]
153 /* Check if address space switch is needed */
154 mov eax, [esi+KTHREAD_APCSTATE_PROCESS]
155 cmp eax, [edi+KTHREAD_APCSTATE_PROCESS]
156 mov eax, [eax+KPROCESS_DIRECTORY_TABLE_BASE]
159 mov [edi+KTHREAD_KERNEL_STACK], esp
160 mov esp, [esi+KTHREAD_KERNEL_STACK]
162 jz NoAddressSpaceSwitch
164 /* Switch address space */
167 NoAddressSpaceSwitch:
169 /* Stack is OK, safe to enable interrupts now */
172 /* Check if address space switch is needed (the result from above is valid) */
173 /* If they match, then use the fast-path and skip all this */
176 /* Get the new Process. */
177 mov edi, [esi+KTHREAD_APCSTATE_PROCESS]
179 /* Check if we need an LDT */
181 cmp [edi+KPROCESS_LDT_DESCRIPTOR0], eax
184 /* Write the LDT Selector */
185 mov ebp, [ebx+KPCR_GDT]
186 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR0]
187 mov [ebp+LDT_SELECTOR], eax
188 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR1]
189 mov [ebp+LDT_SELECTOR+4], eax
191 /* Save LDT Selector */
192 mov eax, LDT_SELECTOR
200 mov ecx, [edi+KPROCESS_IOPM_OFFSET]
202 /* Set current IOPM offset in the TSS */
203 mov [ebp+KTSS_IOMAPBASE], cx
208 mov eax, [esi+KTHREAD_TEB]
209 mov ecx, [ebx+KPCR_GDT]
215 /* Increase context switches */
216 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
218 /* Set TS in cr0 to catch FPU code and load the FPU state when needed */
220 cmp [ebx+KPCR_NPX_THREAD], esi
222 #endif /* !CONFIG_SMP */
228 /* Restore the stack pointer in this processors TSS */
229 pop ss:[ebp+KTSS_ESP0]
231 /* Restore exception list */
232 pop [ebx+KPCR_EXCEPTION_LIST]
235 call @KeReleaseDispatcherDatabaseLockFromDpcLevel@0
241 * The KiSwapContext routine switches context to another thread.
244 * TargetThread - Pointer to the KTHREAD to which the caller wishes to
248 * The WaitStatus of the Target Thread. NOT YET SUPPORTED.
251 * This is a wrapper around KiSwapContextInternal which will save all the
252 * non-volatile registers so that the Internal function can use all of
253 * them. It will also save the old current thread and set the new one.
255 * The calling thread does not return after KiSwapContextInternal until
256 * another thread switches to IT.
259 .globl @KiSwapContext@4
261 /* Note, we CANNOT touch ebp */
263 /* Save 4 registers */
266 /* Save all the non-volatile ones */
272 /* Get the Current Thread */
273 mov edi, fs:[KPCR_CURRENT_THREAD]
275 /* Get the New Thread */
278 /* Save it as Current thread */
279 mov fs:[KPCR_CURRENT_THREAD], esi
281 /* Do the swap with the registers correctly setup */
282 call @KiSwapContextInternal@0
284 /* Return the registers */
297 /* Set up a trap frame */
300 push 0 /* Error Code */ // 0x64
304 /* Fake Interrupt Stack */
305 mov ebp, [esp+20] /* Eip */
306 mov ebx, [esp+16] /* Eflags */
308 mov ebx, [esp+12] /* Cs */
315 push -1 /* Exception List */ // 0x4C
316 push 0 /* Previous Mode */ // 0x48
326 /* Clear breakpoint enables in dr7. */
341 push eax /* TempEsp */
342 push ss /* TempSegSs */
343 push 0 /* DebugPointer */
344 push -1 /* DebugArgMark */
345 push [esp+60] /* Debug EIP */ // 0x4
346 push ebp /* Debug EBP */ // 0x0
351 /* Push old Trap Frame */
352 push [edi+KTHREAD_TRAP_FRAME]
355 mov [edi+KTHREAD_TRAP_FRAME], ebp
357 /* Restore EBP, EBX and EAX */
358 mov ebp, [ebp+KTRAP_FRAME_EBP]
359 mov ebx, [ebp+KTRAP_FRAME_EBX]
360 mov eax, [ebp+KTRAP_FRAME_EAX]
363 push offset RestoreTrapFrameForKDB
365 /* Jump to normal code */
366 jmp SaveTrapFrameForKDB_Return
368 RestoreTrapFrameForKDB:
370 /* Restore the old trapframe */
371 pop [esi+KTHREAD_TRAP_FRAME]
373 /* Pop unused portions of the trap frame */
376 /* Restore registers from Trap frame */
383 add esp, 8 /* ExceptionList and PreviousMode */
389 add esp, 4 /* ErrorCode */
391 /* Return to the caller. */