1/* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/ke/amd64/ctxswitch.S 5 * PURPOSE: Thread Context Switching 6 * 7 * PROGRAMMER: Timo kreuzer (timo.kreuzer@reactos.org) 8 */ 9 10/* INCLUDES ******************************************************************/ 11 12#include <ksamd64.inc> 13 14/* 15 * BOOLEAN 16 * KiSwapContextResume( 17 * _In_ KIRQL WaitIrql, 18 * _In_ PKTHREAD OldThread, 19 * _In_ PKTHREAD NewThread) 20 */ 21EXTERN KiSwapContextResume:PROC 22 23/* FUNCTIONS ****************************************************************/ 24 25.code64 26 27/*! 28 * \name KiThreadStartup 29 * 30 * \brief 31 * The KiThreadStartup routine is the beginning of any thread. 32 * 33 * VOID 34 * KiThreadStartup( 35 * IN PKSTART_ROUTINE StartRoutine<rcx>, 36 * IN PVOID StartContext<rdx>, 37 * IN PVOID P3<r8>, 38 * IN PVOID P4<r9>, 39 * IN PVOID SystemRoutine); 40 * 41 * \param StartRoutine 42 * For Kernel Threads only, specifies the starting execution point 43 * of the new thread. 44 * 45 * \param StartContext 46 * For Kernel Threads only, specifies a pointer to variable 47 * context data to be sent to the StartRoutine above. 48 * 49 * \param P3, P4 - not used atm 50 * 51 * \param SystemRoutine 52 * Pointer to the System Startup Routine. 53 * Either PspUserThreadStartup or PspSystemThreadStartup 54 * 55 * \return 56 * Should never return for a system thread. Returns through the System Call 57 * Exit Dispatcher for a user thread. 58 * 59 * \remarks 60 * If a return from a system thread is detected, a bug check will occur. 61 * 62 *--*/ 63PUBLIC KiThreadStartup 64.PROC KiThreadStartup 65 /* KSTART_FRAME is already on the stack when we enter here. 66 * The virtual prolog looks like this: 67 * sub rsp, 5 * 8 68 * mov [rsp + SfP1Home], rcx 69 * mov [rsp + SfP2Home], rdx 70 * mov [rsp + SfP3Home], r8 71 * mov [rsp + SfP4Home], r9 72 */ 73 74 /* Terminate the unwind chain, by setting rbp as frame pointer, 75 which contains 0 */ 76 .setframe rbp, 0 77 .endprolog 78 79 /* Clear all the non-volatile registers, so the thread won't be tempted to 80 * expect any static data (like some badly coded usermode/win9x apps do) */ 81 xor rbx, rbx 82 xor rsi, rsi 83 xor rdi, rdi 84 xor rbp, rbp 85 xor r10, r10 86 xor r11, r11 87 xor r12, r12 88 xor r13, r13 89 xor r14, r14 90 xor r15, r15 91 92 /* It's now safe to go to APC */ 93 mov rax, APC_LEVEL 94 mov cr8, rax 95 96 /* We have the KSTART_FRAME on the stack, P1Home and P2Home are preloaded 97 * with the parameters for the system routine. The address of the system 98 * routine is stored in P4Home. */ 99 mov rcx, [rsp + SfP1Home] /* StartRoutine */ 100 mov rdx, [rsp + SfP2Home] /* StartContext */ 101 mov r8, [rsp + SfP3Home] /* ? */ 102 call qword ptr [rsp + SfP4Home] /* SystemRoutine */ 103 104 /* The thread returned. If it was a user-thread, we have a return address 105 and all is well, otherwise this is very bad. */ 106 mov rcx, [rsp + SfReturn] 107 or rcx, rcx 108 jnz .leave 109 110 /* A system thread returned...this is very bad! */ 111 int 3 112 113.leave: 114 /* It was a user thread, set our trapframe for the System Call Exit Dispatcher */ 115 lea rcx, [rsp + 6 * 8 + KEXCEPTION_FRAME_LENGTH] 116 117 /* Return to the trap exit code */ 118 add rsp, 5 * 8 119 ret 120.ENDP 121 122/*! 123 * \name KiSwapContextInternal 124 * 125 * \brief 126 * The KiSwapContextInternal routine switches context to another thread. 127 * 128 * \param cl 129 * The IRQL at wich the old thread is suspended 130 * 131 * \param rdx 132 * Pointer to the KTHREAD to which the caller wishes to switch from. 133 * 134 * \param r8 135 * Pointer to the KTHREAD to which the caller wishes to switch to. 136 * 137 * \return 138 * The WaitStatus of the Target Thread. 139 * 140 * \remarks 141 * ... 142 * 143 *--*/ 144.PROC KiSwapContextInternal 145 146 push rbp 147 .pushreg rbp 148 sub rsp, 6 * 8 149 .allocstack (6 * 8) 150 .endprolog 151 152 /* Save WaitIrql as KSWITCH_FRAME::ApcBypass */ 153 mov [rsp + SwApcBypass], cl 154 155 /* Save kernel stack of old thread */ 156 mov [rdx + KTHREAD_KernelStack], rsp 157 158 /* Load stack of new thread */ 159 mov rsp, [r8 + KTHREAD_KernelStack] 160 161 /* Reload APC bypass */ 162 mov cl, [rsp + SwApcBypass] 163 164 call KiSwapContextResume 165 166 /* Cleanup and return */ 167 add rsp, 6 * 8 168 pop rbp 169 ret 170 171.ENDP 172 173 174 175/*! 176 * KiSwapContext 177 * 178 * \brief 179 * The KiSwapContext routine switches context to another thread. 180 * 181 * BOOLEAN 182 * KiSwapContext(KIRQL WaitIrql, PKTHREAD OldThread); 183 * 184 * \param WaitIrql <cl> 185 * The IRQL at wich the old thread is suspended 186 * 187 * \param OldThread <rdx> 188 * Pointer to the KTHREAD of the previous thread. 189 * 190 * \return 191 * The WaitStatus of the Target Thread. 192 * 193 * \remarks 194 * This is a wrapper around KiSwapContextInternal which will save all the 195 * non-volatile registers so that the Internal function can use all of 196 * them. It will also save the old current thread and set the new one. 197 * 198 * The calling thread does not return after KiSwapContextInternal until 199 * another thread switches to IT. 200 * 201 *--*/ 202PUBLIC KiSwapContext 203.PROC KiSwapContext 204 205 /* Generate a KEXCEPTION_FRAME on the stack */ 206 GENERATE_EXCEPTION_FRAME 207 208 /* Do the swap with the registers correctly setup */ 209 mov r8, gs:[PcCurrentThread] /* Pointer to the new thread */ 210 call KiSwapContextInternal 211 212 /* Restore the registers from the KEXCEPTION_FRAME */ 213 RESTORE_EXCEPTION_STATE 214 215 /* Return */ 216 ret 217.ENDP 218 219END 220