2005-04-22 12:52:25 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS kernel
|
|
|
|
* FILE: ntoskrnl/ke/i386/ctxswitch.S
|
|
|
|
* PURPOSE: Thread Context Switching
|
|
|
|
*
|
|
|
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
2005-06-26 15:22:44 +00:00
|
|
|
* Gregor Anich (FPU Code)
|
2005-04-22 12:52:25 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
|
|
|
|
#include <roscfg.h>
|
|
|
|
#include <internal/i386/ke.h>
|
2005-07-20 00:33:06 +00:00
|
|
|
#include <ndk/asm.h>
|
2005-04-22 12:52:25 +00:00
|
|
|
.intel_syntax noprefix
|
|
|
|
|
|
|
|
#define Running 2
|
|
|
|
#define SIZEOF_TRAP_FRAME 0x8c
|
|
|
|
#define APC_LEVEL 1
|
|
|
|
|
|
|
|
/* GLOBALS ****************************************************************/
|
|
|
|
|
2006-01-05 04:26:55 +00:00
|
|
|
.extern _DispatcherDatabaseLock
|
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
|
|
|
|
/*++
|
|
|
|
* KiThreadStartup
|
|
|
|
*
|
|
|
|
* The KiThreadStartup routine is the beginning of any thread.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* SystemRoutine - Pointer to the System Startup Routine. Either
|
|
|
|
* PspUserThreadStartup or PspSystemThreadStartup
|
|
|
|
*
|
|
|
|
* StartRoutine - For Kernel Threads only, specifies the starting execution
|
|
|
|
* point of the new thread.
|
|
|
|
*
|
|
|
|
* StartContext - For Kernel Threads only, specifies a pointer to variable
|
|
|
|
* context data to be sent to the StartRoutine above.
|
|
|
|
*
|
|
|
|
* UserThread - Indicates whether or not this is a user thread. This tells
|
|
|
|
* us if the thread has a context or not.
|
|
|
|
*
|
|
|
|
* TrapFrame - Pointer to the KTHREAD to which the caller wishes to
|
|
|
|
* switch from.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Should never return for a system thread. Returns through the System Call
|
|
|
|
* Exit Dispatcher for a user thread.
|
|
|
|
*
|
|
|
|
* Remarks:
|
|
|
|
* If a return from a system thread is detected, a bug check will occur.
|
|
|
|
*
|
|
|
|
*--*/
|
|
|
|
.globl _KiThreadStartup@156
|
|
|
|
_KiThreadStartup@156:
|
|
|
|
|
2006-01-16 02:21:22 +00:00
|
|
|
/*
|
2005-04-22 12:52:25 +00:00
|
|
|
* Clear all the non-volatile registers, so the thread won't be tempted to
|
|
|
|
* expect any static data (like some badly coded usermode/win9x apps do)
|
|
|
|
*/
|
|
|
|
xor ebx, ebx
|
2005-12-02 20:06:02 +00:00
|
|
|
xor esi, esi
|
2005-04-22 12:52:25 +00:00
|
|
|
xor edi, edi
|
|
|
|
xor ebp, ebp
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* It's now safe to go to APC */
|
|
|
|
mov ecx, APC_LEVEL
|
|
|
|
call @KfLowerIrql@4
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/*
|
|
|
|
* Call the System Routine which is right on our stack now.
|
|
|
|
* After we pop the pointer, the Start Routine/Context will be on the
|
|
|
|
* stack, as parameters to the System Routine
|
|
|
|
*/
|
|
|
|
pop eax
|
|
|
|
call eax
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* The thread returned... was it a user-thread? */
|
|
|
|
pop ecx
|
|
|
|
or ecx, ecx
|
|
|
|
jz BadThread
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Yes it was, set our trapframe for the System Call Exit Dispatcher */
|
|
|
|
mov ebp, esp
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Exit back to user-mode */
|
2005-04-23 05:00:10 +00:00
|
|
|
jmp _KiServiceExit2
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
BadThread:
|
|
|
|
|
|
|
|
/* A system thread returned...this is very bad! */
|
|
|
|
int 3
|
|
|
|
|
|
|
|
/*++
|
|
|
|
* KiSwapContextInternal
|
|
|
|
*
|
|
|
|
* The KiSwapContextInternal routine switches context to another thread.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* ESI - Pointer to the KTHREAD to which the caller wishes to
|
|
|
|
* switch to.
|
|
|
|
* EDI - Pointer to the KTHREAD to which the caller wishes to
|
|
|
|
* switch from.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Remarks:
|
|
|
|
* Absolutely all registers except ESP can be trampled here for maximum code flexibility.
|
|
|
|
*
|
|
|
|
*--*/
|
|
|
|
.globl @KiSwapContextInternal@0
|
|
|
|
@KiSwapContextInternal@0:
|
2005-04-23 04:12:26 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Get the PCR. It's faster to use ebx+offset then fs:offset */
|
2005-04-23 04:12:26 +00:00
|
|
|
mov ebx, [fs:KPCR_SELF]
|
2005-04-22 12:52:25 +00:00
|
|
|
|
|
|
|
/* Set the Thread to running */
|
|
|
|
mov byte ptr [esi+KTHREAD_STATE], Running
|
2005-05-07 00:37:48 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Save the Exception list */
|
|
|
|
push [ebx+KPCR_EXCEPTION_LIST]
|
|
|
|
|
|
|
|
/* Switching, disable interrupts now */
|
|
|
|
cli
|
2005-05-07 00:37:48 +00:00
|
|
|
|
2005-09-13 19:33:49 +00:00
|
|
|
/* Save the initial stack in EAX */
|
2006-01-16 02:21:22 +00:00
|
|
|
mov eax, [esi+KTHREAD_INITIAL_STACK]
|
|
|
|
|
|
|
|
/* Save the stack limit in ecx */
|
|
|
|
mov ecx, [esi+KTHREAD_STACK_LIMIT]
|
|
|
|
|
|
|
|
/* Make space for the NPX Frame */
|
|
|
|
sub eax, NPX_FRAME_LENGTH
|
|
|
|
|
|
|
|
/* Set the KPCR stack values */
|
|
|
|
mov [ebx+KPCR_INITIAL_STACK], eax
|
|
|
|
mov [ebx+KPCR_STACK_LIMIT], ecx
|
2005-09-13 19:33:49 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
/* Save FPU state if the thread has used it. */
|
2006-01-16 02:21:22 +00:00
|
|
|
mov ecx, [edi+KTHREAD_INITIAL_STACK]
|
|
|
|
sub ecx, NPX_FRAME_LENGTH
|
2005-04-23 12:44:42 +00:00
|
|
|
mov dword ptr [ebx+KPCR_NPX_THREAD], 0
|
2005-04-22 12:52:25 +00:00
|
|
|
test byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_DIRTY
|
|
|
|
jz 3f
|
2006-01-15 10:17:35 +00:00
|
|
|
cmp dword ptr _KeI386FxsrPresent, 0
|
2005-04-22 12:52:25 +00:00
|
|
|
je 1f
|
2006-01-16 02:21:22 +00:00
|
|
|
fxsave [ecx]
|
2005-04-22 12:52:25 +00:00
|
|
|
jmp 2f
|
|
|
|
1:
|
2006-01-16 02:21:22 +00:00
|
|
|
fnsave [ecx]
|
2005-04-22 12:52:25 +00:00
|
|
|
2:
|
2005-04-23 12:44:42 +00:00
|
|
|
mov byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_VALID
|
2005-04-22 12:52:25 +00:00
|
|
|
3:
|
|
|
|
#endif /* CONFIG_SMP */
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Save the stack pointer in this processors TSS */
|
|
|
|
mov ebp, [ebx+KPCR_TSS]
|
2005-09-13 19:33:49 +00:00
|
|
|
|
|
|
|
/* Check if this isn't V86 Mode, so we can bias the Esp0 */
|
|
|
|
test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
|
|
|
|
jnz NoAdjust
|
|
|
|
|
|
|
|
/* Bias esp */
|
2006-01-16 02:21:22 +00:00
|
|
|
sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
|
2005-09-13 19:33:49 +00:00
|
|
|
|
|
|
|
NoAdjust:
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2006-01-16 17:05:50 +00:00
|
|
|
/* Save it */
|
|
|
|
push [ebp+KTSS_ESP0]
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2006-01-16 17:07:45 +00:00
|
|
|
/* Set new ESP0 */
|
|
|
|
mov [ebp+KTSS_ESP0], eax
|
|
|
|
|
2006-01-16 02:21:22 +00:00
|
|
|
/* Set TEB pointer */
|
|
|
|
mov eax, [esi+KTHREAD_TEB]
|
|
|
|
mov [ebx+KPCR_TEB], eax
|
2005-09-13 19:33:49 +00:00
|
|
|
|
2005-05-08 15:59:07 +00:00
|
|
|
/* Check if address space switch is needed */
|
|
|
|
mov eax, [esi+KTHREAD_APCSTATE_PROCESS]
|
|
|
|
cmp eax, [edi+KTHREAD_APCSTATE_PROCESS]
|
|
|
|
mov eax, [eax+KPROCESS_DIRECTORY_TABLE_BASE]
|
2005-04-22 12:52:25 +00:00
|
|
|
|
|
|
|
/* Switch stacks */
|
|
|
|
mov [edi+KTHREAD_KERNEL_STACK], esp
|
|
|
|
mov esp, [esi+KTHREAD_KERNEL_STACK]
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-05-08 15:59:07 +00:00
|
|
|
jz NoAddressSpaceSwitch
|
2006-01-16 02:21:22 +00:00
|
|
|
|
|
|
|
/* Clear gs */
|
|
|
|
xor ecx, ecx
|
|
|
|
mov gs, cx
|
|
|
|
|
2005-05-08 15:59:07 +00:00
|
|
|
/* Switch address space */
|
|
|
|
mov cr3, eax
|
2006-01-16 02:21:22 +00:00
|
|
|
mov [ebp+KTSS_CR3], eax
|
|
|
|
|
|
|
|
NoAddressSpaceSwitch:
|
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Stack is OK, safe to enable interrupts now */
|
2006-01-16 02:21:22 +00:00
|
|
|
sti
|
2005-05-07 00:37:48 +00:00
|
|
|
|
2005-05-08 15:59:07 +00:00
|
|
|
/* Check if address space switch is needed (the result from above is valid) */
|
2005-04-22 12:52:25 +00:00
|
|
|
/* If they match, then use the fast-path and skip all this */
|
|
|
|
jz SameProcess
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Get the new Process. */
|
|
|
|
mov edi, [esi+KTHREAD_APCSTATE_PROCESS]
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Check if we need an LDT */
|
|
|
|
xor eax, eax
|
|
|
|
cmp [edi+KPROCESS_LDT_DESCRIPTOR0], eax
|
|
|
|
jz NoLdt
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Write the LDT Selector */
|
2006-01-16 02:21:22 +00:00
|
|
|
mov ecx, [ebx+KPCR_GDT]
|
2005-04-22 12:52:25 +00:00
|
|
|
mov eax, [edi+KPROCESS_LDT_DESCRIPTOR0]
|
2006-01-16 02:21:22 +00:00
|
|
|
mov [ecx+KGDT_LDT], eax
|
2005-04-22 12:52:25 +00:00
|
|
|
mov eax, [edi+KPROCESS_LDT_DESCRIPTOR1]
|
2006-01-16 02:21:22 +00:00
|
|
|
mov [ecx+KGDT_LDT+4], eax
|
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Save LDT Selector */
|
2005-11-27 03:08:35 +00:00
|
|
|
mov eax, KGDT_LDT
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
NoLdt:
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Load LDT */
|
|
|
|
lldt ax
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Get the IOPM */
|
|
|
|
mov ecx, [edi+KPROCESS_IOPM_OFFSET]
|
|
|
|
|
|
|
|
/* Set current IOPM offset in the TSS */
|
|
|
|
mov [ebp+KTSS_IOMAPBASE], cx
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
SameProcess:
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Set the TEB */
|
|
|
|
mov eax, [esi+KTHREAD_TEB]
|
|
|
|
mov ecx, [ebx+KPCR_GDT]
|
|
|
|
mov [ecx+0x3A], ax
|
|
|
|
shr eax, 16
|
|
|
|
mov [ecx+0x3C], al
|
|
|
|
mov [ecx+0x3F], ah
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Increase context switches */
|
|
|
|
inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Set TS in cr0 to catch FPU code and load the FPU state when needed */
|
|
|
|
#ifndef CONFIG_SMP
|
|
|
|
cmp [ebx+KPCR_NPX_THREAD], esi
|
|
|
|
je 4f
|
|
|
|
#endif /* !CONFIG_SMP */
|
|
|
|
mov eax, cr0
|
|
|
|
or eax, X86_CR0_TS
|
|
|
|
mov cr0, eax
|
|
|
|
4:
|
2006-01-16 02:21:22 +00:00
|
|
|
|
2006-01-16 17:05:50 +00:00
|
|
|
/* Restore ESP0 */
|
|
|
|
pop [ebp+KTSS_ESP0]
|
|
|
|
|
2005-04-22 12:52:25 +00:00
|
|
|
/* Restore exception list */
|
|
|
|
pop [ebx+KPCR_EXCEPTION_LIST]
|
|
|
|
|
|
|
|
/* Return */
|
2006-01-05 13:59:11 +00:00
|
|
|
#ifdef CONFIG_SMP
|
2006-01-05 12:48:33 +00:00
|
|
|
mov ecx, offset _DispatcherDatabaseLock
|
2006-01-05 04:26:55 +00:00
|
|
|
call @KefReleaseSpinLockFromDpcLevel@4
|
2006-01-05 13:59:11 +00:00
|
|
|
#endif
|
2005-04-22 12:52:25 +00:00
|
|
|
ret
|
|
|
|
|
|
|
|
/*++
|
|
|
|
* KiSwapContext
|
|
|
|
*
|
|
|
|
* The KiSwapContext routine switches context to another thread.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* TargetThread - Pointer to the KTHREAD to which the caller wishes to
|
|
|
|
* switch to.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* The WaitStatus of the Target Thread. NOT YET SUPPORTED.
|
|
|
|
*
|
|
|
|
* Remarks:
|
|
|
|
* This is a wrapper around KiSwapContextInternal which will save all the
|
|
|
|
* non-volatile registers so that the Internal function can use all of
|
|
|
|
* them. It will also save the old current thread and set the new one.
|
|
|
|
*
|
|
|
|
* The calling thread does not return after KiSwapContextInternal until
|
|
|
|
* another thread switches to IT.
|
|
|
|
*
|
|
|
|
*--*/
|
|
|
|
.globl @KiSwapContext@4
|
|
|
|
@KiSwapContext@4:
|
|
|
|
/* Note, we CANNOT touch ebp */
|
|
|
|
|
|
|
|
/* Save 4 registers */
|
|
|
|
sub esp, 4 * 4
|
|
|
|
|
|
|
|
/* Save all the non-volatile ones */
|
|
|
|
mov [esp+12], ebx
|
|
|
|
mov [esp+8], esi
|
|
|
|
mov [esp+4], edi
|
|
|
|
mov [esp+0], ebp
|
|
|
|
|
|
|
|
/* Get the Current Thread */
|
|
|
|
mov edi, fs:[KPCR_CURRENT_THREAD]
|
|
|
|
|
|
|
|
/* Get the New Thread */
|
|
|
|
mov esi, ecx
|
|
|
|
|
|
|
|
/* Save it as Current thread */
|
|
|
|
mov fs:[KPCR_CURRENT_THREAD], esi
|
|
|
|
|
|
|
|
/* Do the swap with the registers correctly setup */
|
|
|
|
call @KiSwapContextInternal@0
|
|
|
|
|
|
|
|
/* Return the registers */
|
|
|
|
mov ebp, [esp+0]
|
2005-04-23 16:09:06 +00:00
|
|
|
mov edi, [esp+4]
|
|
|
|
mov esi, [esp+8]
|
2005-04-22 12:52:25 +00:00
|
|
|
mov ebx, [esp+12]
|
|
|
|
|
|
|
|
/* Clean stack */
|
|
|
|
add esp, 4 * 4
|
|
|
|
ret
|
|
|
|
|
2006-01-17 06:36:35 +00:00
|
|
|
.globl _Ki386AdjustEsp0@4
|
|
|
|
.func Ki386AdjustEsp0@4
|
|
|
|
_Ki386AdjustEsp0@4:
|
|
|
|
|
|
|
|
/* Get the current thread */
|
|
|
|
mov eax, [fs:KPCR_CURRENT_THREAD]
|
|
|
|
|
|
|
|
/* Get trap frame and stack */
|
|
|
|
mov edx, [esp+4]
|
|
|
|
mov eax, [eax+KTHREAD_INITIAL_STACK]
|
|
|
|
|
|
|
|
/* Check if V86 */
|
|
|
|
test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
|
|
|
|
jnz 1f
|
|
|
|
|
|
|
|
/* Bias the stack */
|
|
|
|
sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
|
|
|
|
|
|
|
|
1:
|
|
|
|
/* Skip FX Save Area */
|
|
|
|
sub eax, SIZEOF_FX_SAVE_AREA
|
|
|
|
|
|
|
|
/* Disable interrupts */
|
|
|
|
pushf
|
|
|
|
cli
|
|
|
|
|
|
|
|
/* Adjust ESP0 */
|
|
|
|
mov edx, [fs:KPCR_TSS]
|
|
|
|
mov ss:[edx+KTSS_ESP0], eax
|
|
|
|
|
|
|
|
/* Enable interrupts and return */
|
|
|
|
popf
|
|
|
|
ret 4
|
|
|
|
.endfunc
|