* Sync up to trunk head (r60691).
[reactos.git] / ntoskrnl / ke / amd64 / ctxswitch.S
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
15 EXTERN 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 *--*/
57 PUBLIC 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 *--*/
138 PUBLIC 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 *--*/
202 PUBLIC 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
268 END