/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/amd64/ctxswitch.S * PURPOSE: Thread Context Switching * * PROGRAMMER: Timo kreuzer (timo.kreuzer@reactos.org) */ /* INCLUDES ******************************************************************/ #include #include EXTERN KiSwapContextResume:PROC /* FUNCTIONS ****************************************************************/ .code64 /*! * \name KiThreadStartup * * \brief * The KiThreadStartup routine is the beginning of any thread. * * VOID * KiThreadStartup( * IN PKSTART_ROUTINE StartRoutine, * IN PVOID StartContext, * IN PVOID P3, * IN PVOID P4, * IN PVOID SystemRoutine); * * \param StartRoutine * For Kernel Threads only, specifies the starting execution point * of the new thread. * * \param StartContext * For Kernel Threads only, specifies a pointer to variable * context data to be sent to the StartRoutine above. * * \param P3, P4 - not used atm * * \param SystemRoutine * Pointer to the System Startup Routine. * Either PspUserThreadStartup or PspSystemThreadStartup * * \return * 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. * *--*/ PUBLIC KiThreadStartup .PROC KiThreadStartup /* KSTART_FRAME is already on the stack when we enter here. * The virtual prolog looks like this: * sub rsp, 5 * 8 * mov [rsp + SfP1Home], rcx * mov [rsp + SfP2Home], rdx * mov [rsp + SfP3Home], r8 * mov [rsp + SfP4Home], r9 */ /* Terminate the unwind chain, by setting rbp as frame pointer, which contains 0 */ .setframe rbp, 0 .endprolog /* 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 rbx, rbx xor rsi, rsi xor rdi, rdi xor rbp, rbp xor r10, r10 xor r11, r11 xor r12, r12 xor r13, r13 xor r14, r14 xor r15, r15 /* It's now safe to go to APC */ mov rax, APC_LEVEL mov cr8, rax /* We have the KSTART_FRAME on the stack, P1Home and P2Home are preloaded * with the parameters for the system routine. The address of the system * routine is stored in P4Home. */ mov rcx, [rsp + SfP1Home] /* StartRoutine */ mov rdx, [rsp + SfP2Home] /* StartContext */ mov r8, [rsp + SfP3Home] /* ? */ call qword ptr [rsp + SfP4Home] /* SystemRoutine */ /* The thread returned. If it was a user-thread, we have a return address and all is well, otherwise this is very bad. */ mov rcx, [rsp + SfReturn] or rcx, rcx jnz .leave /* A system thread returned...this is very bad! */ int 3 .leave: /* It was a user thread, set our trapframe for the System Call Exit Dispatcher */ lea rcx, [rsp + 6 * 8 + KEXCEPTION_FRAME_LENGTH] /* Return to the trap exit code */ add rsp, 5 * 8 ret .ENDP /*! * \name KiSwapContextInternal * * \brief * The KiSwapContextInternal routine switches context to another thread. * * \param rcx * Pointer to the KTHREAD to which the caller wishes to switch to. * * \param rdx * Pointer to the KTHREAD to which the caller wishes to switch from. * * \param r8b * APC bypass * * \return * None. * * \remarks * ... * *--*/ PUBLIC KiSwapContextInternal .PROC KiSwapContextInternal push rbp .pushreg rbp sub rsp, 6 * 8 .allocstack 6 * 8 .endprolog /* Save APC bypass */ mov [rsp + SwApcBypass], r8b /* Save kernel stack of old thread */ mov [rdx + KTHREAD_KernelStack], rsp /* Save new thread in rbp */ mov rbp, rcx //call KiSwapContextSuspend /* Load stack of new thread */ mov rsp, [rbp + KTHREAD_KernelStack] /* Reload APC bypass */ mov r8b, [rsp + SwApcBypass] call KiSwapContextResume /* Cleanup and return */ add rsp, 6 * 8 pop rbp ret .ENDP /*! * KiSwapContext * * \brief * The KiSwapContext routine switches context to another thread. * * BOOLEAN * KiSwapContext(KIRQL WaitIrql, PKTHREAD CurrentThread); * * \param WaitIrql * ... * * \param CurrentThread * Pointer to the KTHREAD of the current thread. * * \return * The WaitStatus of the Target Thread. * * \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. * *--*/ PUBLIC KiSwapContext .PROC KiSwapContext /* Allocate a KEXCEPTION_FRAME on the stack (+8 for proper alignment) */ sub rsp, KEXCEPTION_FRAME_LENGTH + 8 .allocstack KEXCEPTION_FRAME_LENGTH + 8 /* save non-volatiles in KEXCEPTION_FRAME */ mov [rsp + KEXCEPTION_FRAME_Rbp], rbp .savereg rbp, KEXCEPTION_FRAME_Rbp mov [rsp + KEXCEPTION_FRAME_Rbx], rbx .savereg rbx, KEXCEPTION_FRAME_Rbx mov [rsp + KEXCEPTION_FRAME_Rdi], rdi .savereg rdi, KEXCEPTION_FRAME_Rdi mov [rsp + KEXCEPTION_FRAME_Rsi], rsi .savereg rsi, KEXCEPTION_FRAME_Rsi mov [rsp + KEXCEPTION_FRAME_R12], r12 .savereg r12, KEXCEPTION_FRAME_R12 mov [rsp + KEXCEPTION_FRAME_R13], r13 .savereg r13, KEXCEPTION_FRAME_R13 mov [rsp + KEXCEPTION_FRAME_R14], r14 .savereg r14, KEXCEPTION_FRAME_R14 mov [rsp + KEXCEPTION_FRAME_R15], r15 .savereg r15, KEXCEPTION_FRAME_R15 movdqa [rsp + KEXCEPTION_FRAME_Xmm6], xmm6 movdqa [rsp + KEXCEPTION_FRAME_Xmm7], xmm7 movdqa [rsp + KEXCEPTION_FRAME_Xmm8], xmm8 movdqa [rsp + KEXCEPTION_FRAME_Xmm9], xmm9 movdqa [rsp + KEXCEPTION_FRAME_Xmm10], xmm10 movdqa [rsp + KEXCEPTION_FRAME_Xmm11], xmm11 movdqa [rsp + KEXCEPTION_FRAME_Xmm12], xmm12 movdqa [rsp + KEXCEPTION_FRAME_Xmm13], xmm13 movdqa [rsp + KEXCEPTION_FRAME_Xmm14], xmm14 movdqa [rsp + KEXCEPTION_FRAME_Xmm15], xmm15 // KEXCEPTION_FRAME_MxCsr .endprolog /* Do the swap with the registers correctly setup */ mov rcx, gs:[PcCurrentThread] /* Pointer to the new thread */ call KiSwapContextInternal /* restore non-volatile registers */ mov rbp, [rsp + KEXCEPTION_FRAME_Rbp] mov rbx, [rsp + KEXCEPTION_FRAME_Rbx] mov rdi, [rsp + KEXCEPTION_FRAME_Rdi] mov rsi, [rsp + KEXCEPTION_FRAME_Rsi] mov r12, [rsp + KEXCEPTION_FRAME_R12] mov r13, [rsp + KEXCEPTION_FRAME_R13] mov r14, [rsp + KEXCEPTION_FRAME_R14] mov r15, [rsp + KEXCEPTION_FRAME_R15] movdqa xmm6, [rsp + KEXCEPTION_FRAME_Xmm6] movdqa xmm7, [rsp + KEXCEPTION_FRAME_Xmm7] movdqa xmm8, [rsp + KEXCEPTION_FRAME_Xmm8] movdqa xmm9, [rsp + KEXCEPTION_FRAME_Xmm9] movdqa xmm10, [rsp + KEXCEPTION_FRAME_Xmm10] movdqa xmm11, [rsp + KEXCEPTION_FRAME_Xmm11] movdqa xmm12, [rsp + KEXCEPTION_FRAME_Xmm12] movdqa xmm13, [rsp + KEXCEPTION_FRAME_Xmm13] movdqa xmm14, [rsp + KEXCEPTION_FRAME_Xmm14] movdqa xmm15, [rsp + KEXCEPTION_FRAME_Xmm15] /* Clean stack and return */ add rsp, KEXCEPTION_FRAME_LENGTH + 8 ret .ENDP END