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 <asm.inc> 13#include <ksamd64.inc> 14 15EXTERN KiSwapContextResume:PROC 16 17/* FUNCTIONS ****************************************************************/ 18 19.code64 20 21/*! 22 * \name KiThreadStartup 23 * 24 * \brief 25 * The KiThreadStartup routine is the beginning of any thread. 26 * 27 * VOID 28 * KiThreadStartup( 29 * IN PKSTART_ROUTINE StartRoutine<rcx>, 30 * IN PVOID StartContext<rdx>, 31 * IN PVOID P3<r8>, 32 * IN PVOID P4<r9>, 33 * IN PVOID SystemRoutine); 34 * 35 * \param StartRoutine 36 * For Kernel Threads only, specifies the starting execution point 37 * of the new thread. 38 * 39 * \param StartContext 40 * For Kernel Threads only, specifies a pointer to variable 41 * context data to be sent to the StartRoutine above. 42 * 43 * \param P3, P4 - not used atm 44 * 45 * \param SystemRoutine 46 * Pointer to the System Startup Routine. 47 * Either PspUserThreadStartup or PspSystemThreadStartup 48 * 49 * \return 50 * Should never return for a system thread. Returns through the System Call 51 * Exit Dispatcher for a user thread. 52 * 53 * \remarks 54 * If a return from a system thread is detected, a bug check will occur. 55 * 56 *--*/ 57PUBLIC KiThreadStartup 58.PROC KiThreadStartup 59 /* KSTART_FRAME is already on the stack when we enter here. 60 * The virtual prolog looks like this: 61 * sub rsp, 5 * 8 62 * mov [rsp + SfP1Home], rcx 63 * mov [rsp + SfP2Home], rdx 64 * mov [rsp + SfP3Home], r8 65 * mov [rsp + SfP4Home], r9 66 */ 67 68 /* Terminate the unwind chain, by setting rbp as frame pointer, 69 which contains 0 */ 70 .setframe rbp, 0 71 .endprolog 72 73 /* Clear all the non-volatile registers, so the thread won't be tempted to 74 * expect any static data (like some badly coded usermode/win9x apps do) */ 75 xor rbx, rbx 76 xor rsi, rsi 77 xor rdi, rdi 78 xor rbp, rbp 79 xor r10, r10 80 xor r11, r11 81 xor r12, r12 82 xor r13, r13 83 xor r14, r14 84 xor r15, r15 85 86 /* It's now safe to go to APC */ 87 mov rax, APC_LEVEL 88 mov cr8, rax 89 90 /* We have the KSTART_FRAME on the stack, P1Home and P2Home are preloaded 91 * with the parameters for the system routine. The address of the system 92 * routine is stored in P4Home. */ 93 mov rcx, [rsp + SfP1Home] /* StartRoutine */ 94 mov rdx, [rsp + SfP2Home] /* StartContext */ 95 mov r8, [rsp + SfP3Home] /* ? */ 96 call qword ptr [rsp + SfP4Home] /* SystemRoutine */ 97 98 /* The thread returned. If it was a user-thread, we have a return address 99 and all is well, otherwise this is very bad. */ 100 mov rcx, [rsp + SfReturn] 101 or rcx, rcx 102 jnz .leave 103 104 /* A system thread returned...this is very bad! */ 105 int 3 106 107.leave: 108 /* It was a user thread, set our trapframe for the System Call Exit Dispatcher */ 109 lea rcx, [rsp + 6 * 8 + KEXCEPTION_FRAME_LENGTH] 110 111 /* Return to the trap exit code */ 112 add rsp, 5 * 8 113 ret 114.ENDP 115 116/*! 117 * \name KiSwapContextInternal 118 * 119 * \brief 120 * The KiSwapContextInternal routine switches context to another thread. 121 * 122 * \param rcx 123 * Pointer to the KTHREAD to which the caller wishes to switch to. 124 * 125 * \param rdx 126 * Pointer to the KTHREAD to which the caller wishes to switch from. 127 * 128 * \param r8b 129 * APC bypass 130 * 131 * \return 132 * None. 133 * 134 * \remarks 135 * ... 136 * 137 *--*/ 138PUBLIC KiSwapContextInternal 139.PROC KiSwapContextInternal 140 141 push rbp 142 .pushreg rbp 143 sub rsp, 6 * 8 144 .allocstack 6 * 8 145 .endprolog 146 147 /* Save APC bypass */ 148 mov [rsp + SwApcBypass], r8b 149 150 /* Save kernel stack of old thread */ 151 mov [rdx + KTHREAD_KernelStack], rsp 152 153 /* Save new thread in rbp */ 154 mov rbp, rcx 155 156 //call KiSwapContextSuspend 157 158 /* Load stack of new thread */ 159 mov rsp, [rbp + KTHREAD_KernelStack] 160 161 /* Reload APC bypass */ 162 mov r8b, [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 CurrentThread); 183 * 184 * \param WaitIrql <rcx> 185 * ... 186 * 187 * \param CurrentThread <rdx> 188 * Pointer to the KTHREAD of the current 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 /* Allocate a KEXCEPTION_FRAME on the stack (+8 for proper alignment) */ 206 sub rsp, KEXCEPTION_FRAME_LENGTH + 8 207 .allocstack KEXCEPTION_FRAME_LENGTH + 8 208 209 /* save non-volatiles in KEXCEPTION_FRAME */ 210 mov [rsp + KEXCEPTION_FRAME_Rbp], rbp 211 .savereg rbp, KEXCEPTION_FRAME_Rbp 212 mov [rsp + KEXCEPTION_FRAME_Rbx], rbx 213 .savereg rbx, KEXCEPTION_FRAME_Rbx 214 mov [rsp + KEXCEPTION_FRAME_Rdi], rdi 215 .savereg rdi, KEXCEPTION_FRAME_Rdi 216 mov [rsp + KEXCEPTION_FRAME_Rsi], rsi 217 .savereg rsi, KEXCEPTION_FRAME_Rsi 218 mov [rsp + KEXCEPTION_FRAME_R12], r12 219 .savereg r12, KEXCEPTION_FRAME_R12 220 mov [rsp + KEXCEPTION_FRAME_R13], r13 221 .savereg r13, KEXCEPTION_FRAME_R13 222 mov [rsp + KEXCEPTION_FRAME_R14], r14 223 .savereg r14, KEXCEPTION_FRAME_R14 224 mov [rsp + KEXCEPTION_FRAME_R15], r15 225 .savereg r15, KEXCEPTION_FRAME_R15 226 movdqa [rsp + KEXCEPTION_FRAME_Xmm6], xmm6 227 movdqa [rsp + KEXCEPTION_FRAME_Xmm7], xmm7 228 movdqa [rsp + KEXCEPTION_FRAME_Xmm8], xmm8 229 movdqa [rsp + KEXCEPTION_FRAME_Xmm9], xmm9 230 movdqa [rsp + KEXCEPTION_FRAME_Xmm10], xmm10 231 movdqa [rsp + KEXCEPTION_FRAME_Xmm11], xmm11 232 movdqa [rsp + KEXCEPTION_FRAME_Xmm12], xmm12 233 movdqa [rsp + KEXCEPTION_FRAME_Xmm13], xmm13 234 movdqa [rsp + KEXCEPTION_FRAME_Xmm14], xmm14 235 movdqa [rsp + KEXCEPTION_FRAME_Xmm15], xmm15 236 // KEXCEPTION_FRAME_MxCsr 237 .endprolog 238 239 /* Do the swap with the registers correctly setup */ 240 mov rcx, gs:[PcCurrentThread] /* Pointer to the new thread */ 241 call KiSwapContextInternal 242 243 /* restore non-volatile registers */ 244 mov rbp, [rsp + KEXCEPTION_FRAME_Rbp] 245 mov rbx, [rsp + KEXCEPTION_FRAME_Rbx] 246 mov rdi, [rsp + KEXCEPTION_FRAME_Rdi] 247 mov rsi, [rsp + KEXCEPTION_FRAME_Rsi] 248 mov r12, [rsp + KEXCEPTION_FRAME_R12] 249 mov r13, [rsp + KEXCEPTION_FRAME_R13] 250 mov r14, [rsp + KEXCEPTION_FRAME_R14] 251 mov r15, [rsp + KEXCEPTION_FRAME_R15] 252 movdqa xmm6, [rsp + KEXCEPTION_FRAME_Xmm6] 253 movdqa xmm7, [rsp + KEXCEPTION_FRAME_Xmm7] 254 movdqa xmm8, [rsp + KEXCEPTION_FRAME_Xmm8] 255 movdqa xmm9, [rsp + KEXCEPTION_FRAME_Xmm9] 256 movdqa xmm10, [rsp + KEXCEPTION_FRAME_Xmm10] 257 movdqa xmm11, [rsp + KEXCEPTION_FRAME_Xmm11] 258 movdqa xmm12, [rsp + KEXCEPTION_FRAME_Xmm12] 259 movdqa xmm13, [rsp + KEXCEPTION_FRAME_Xmm13] 260 movdqa xmm14, [rsp + KEXCEPTION_FRAME_Xmm14] 261 movdqa xmm15, [rsp + KEXCEPTION_FRAME_Xmm15] 262 263 /* Clean stack and return */ 264 add rsp, KEXCEPTION_FRAME_LENGTH + 8 265 ret 266.ENDP 267 268END 269