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 /* Wait for SwapBusy */ 153.SwapBusySet: 154 cmp byte ptr [r8 + ThSwapBusy], 0 155 je .SwapBusyClear 156 pause 157 jmp .SwapBusySet 158.SwapBusyClear: 159 160 /* Save WaitIrql as KSWITCH_FRAME::ApcBypass */ 161 mov [rsp + SwApcBypass], cl 162 163 /* Save kernel stack of old thread */ 164 mov [rdx + KTHREAD_KernelStack], rsp 165 166 /* Load stack of new thread */ 167 mov rsp, [r8 + KTHREAD_KernelStack] 168 169 /* Reload APC bypass */ 170 mov cl, [rsp + SwApcBypass] 171 172 call KiSwapContextResume 173 174 /* Cleanup and return */ 175 add rsp, 6 * 8 176 pop rbp 177 ret 178 179.ENDP 180 181 182 183/*! 184 * KiSwapContext 185 * 186 * \brief 187 * The KiSwapContext routine switches context to another thread. 188 * 189 * BOOLEAN 190 * KiSwapContext(KIRQL WaitIrql, PKTHREAD OldThread); 191 * 192 * \param WaitIrql <cl> 193 * The IRQL at wich the old thread is suspended 194 * 195 * \param OldThread <rdx> 196 * Pointer to the KTHREAD of the previous thread. 197 * 198 * \return 199 * The WaitStatus of the Target Thread. 200 * 201 * \remarks 202 * This is a wrapper around KiSwapContextInternal which will save all the 203 * non-volatile registers so that the Internal function can use all of 204 * them. It will also save the old current thread and set the new one. 205 * 206 * The calling thread does not return after KiSwapContextInternal until 207 * another thread switches to IT. 208 * 209 *--*/ 210PUBLIC KiSwapContext 211.PROC KiSwapContext 212 213 /* Generate a KEXCEPTION_FRAME on the stack */ 214 GENERATE_EXCEPTION_FRAME 215 216 /* Do the swap with the registers correctly setup */ 217 mov r8, gs:[PcCurrentThread] /* Pointer to the new thread */ 218 call KiSwapContextInternal 219 220 /* Restore the registers from the KEXCEPTION_FRAME */ 221 RESTORE_EXCEPTION_STATE 222 223 /* Return */ 224 ret 225.ENDP 226 227END 228