xref: /reactos/ntoskrnl/ke/amd64/ctxswitch.S (revision c2c66aff)
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