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