xref: /reactos/ntoskrnl/ke/amd64/ctxswitch.S (revision 8a978a17)
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; BOOLEAN
15; KiSwapContextResume(
16;    _In_ KIRQL WaitIrql,
17;    _In_ PKTHREAD OldThread,
18;    _In_ PKTHREAD NewThread)
19EXTERN KiSwapContextResume:PROC
20
21/* FUNCTIONS ****************************************************************/
22
23.code64
24
25/*!
26 * \name KiThreadStartup
27 *
28 * \brief
29 *     The KiThreadStartup routine is the beginning of any thread.
30 *
31 * VOID
32 * KiThreadStartup(
33 *     IN PKSTART_ROUTINE StartRoutine<rcx>,
34 *     IN PVOID StartContext<rdx>,
35 *     IN PVOID P3<r8>,
36 *     IN PVOID P4<r9>,
37 *     IN PVOID SystemRoutine);
38 *
39 * \param StartRoutine
40 *     For Kernel Threads only, specifies the starting execution point
41 *     of the new thread.
42 *
43 * \param StartContext
44 *     For Kernel Threads only, specifies a pointer to variable
45 *     context data to be sent to the StartRoutine above.
46 *
47 * \param P3, P4 - not used atm
48 *
49 * \param SystemRoutine
50 *     Pointer to the System Startup Routine.
51 *     Either PspUserThreadStartup or PspSystemThreadStartup
52 *
53 * \return
54 *     Should never return for a system thread. Returns through the System Call
55 *     Exit Dispatcher for a user thread.
56 *
57 * \remarks
58 *     If a return from a system thread is detected, a bug check will occur.
59 *
60 *--*/
61PUBLIC KiThreadStartup
62.PROC KiThreadStartup
63    /* KSTART_FRAME is already on the stack when we enter here.
64     * The virtual prolog looks like this:
65     * sub rsp, 5 * 8
66     * mov [rsp + SfP1Home], rcx
67     * mov [rsp + SfP2Home], rdx
68     * mov [rsp + SfP3Home], r8
69     * mov [rsp + SfP4Home], r9
70     */
71
72    /* Terminate the unwind chain, by setting rbp as frame pointer,
73       which contains 0 */
74    .setframe rbp, 0
75    .endprolog
76
77    /* Clear all the non-volatile registers, so the thread won't be tempted to
78     * expect any static data (like some badly coded usermode/win9x apps do) */
79    xor rbx, rbx
80    xor rsi, rsi
81    xor rdi, rdi
82    xor rbp, rbp
83    xor r10, r10
84    xor r11, r11
85    xor r12, r12
86    xor r13, r13
87    xor r14, r14
88    xor r15, r15
89
90    /* It's now safe to go to APC */
91    mov rax, APC_LEVEL
92    mov cr8, rax
93
94    /* We have the KSTART_FRAME on the stack, P1Home and P2Home are preloaded
95     * with the parameters for the system routine. The address of the system
96     * routine is stored in P4Home. */
97    mov rcx, [rsp + SfP1Home] /* StartRoutine */
98    mov rdx, [rsp + SfP2Home] /* StartContext */
99    mov r8, [rsp + SfP3Home]  /* ? */
100    call qword ptr [rsp + SfP4Home]     /* SystemRoutine */
101
102    /* The thread returned. If it was a user-thread, we have a return address
103       and all is well, otherwise this is very bad. */
104    mov rcx, [rsp + SfReturn]
105    or rcx, rcx
106    jnz .leave
107
108    /* A system thread returned...this is very bad! */
109    int 3
110
111.leave:
112    /* It was a user thread, set our trapframe for the System Call Exit Dispatcher */
113    lea rcx, [rsp + 6 * 8 + KEXCEPTION_FRAME_LENGTH]
114
115    /* Return to the trap exit code */
116    add rsp, 5 * 8
117    ret
118.ENDP
119
120/*!
121 * \name KiSwapContextInternal
122 *
123 * \brief
124 *     The KiSwapContextInternal routine switches context to another thread.
125 *
126 * \param cl
127 *     The IRQL at wich the old thread is suspended
128 *
129 * \param rdx
130 *     Pointer to the KTHREAD to which the caller wishes to switch from.
131 *
132 * \param r8
133 *     Pointer to the KTHREAD to which the caller wishes to switch to.
134 *
135 * \return
136 *     The WaitStatus of the Target Thread.
137 *
138 * \remarks
139 *     ...
140 *
141 *--*/
142.PROC KiSwapContextInternal
143
144    push rbp
145    .pushreg rbp
146    sub rsp, 6 * 8
147    .allocstack 6 * 8
148    .endprolog
149
150    /* Save WaitIrql as KSWITCH_FRAME::ApcBypass */
151    mov [rsp + SwApcBypass], cl
152
153    /* Save kernel stack of old thread */
154    mov [rdx + KTHREAD_KernelStack], rsp
155
156    /* Load stack of new thread */
157    mov rsp, [r8 + KTHREAD_KernelStack]
158
159    /* Reload APC bypass */
160    mov cl, [rsp + SwApcBypass]
161
162    call KiSwapContextResume
163
164    /* Cleanup and return */
165    add rsp, 6 * 8
166    pop rbp
167    ret
168
169.ENDP
170
171
172
173/*!
174 * KiSwapContext
175 *
176 * \brief
177 *     The KiSwapContext routine switches context to another thread.
178 *
179 * BOOLEAN
180 * KiSwapContext(KIRQL WaitIrql, PKTHREAD OldThread);
181 *
182 * \param WaitIrql <cl>
183 *     The IRQL at wich the old thread is suspended
184 *
185 * \param OldThread <rdx>
186 *     Pointer to the KTHREAD of the previous thread.
187 *
188 * \return
189 *     The WaitStatus of the Target Thread.
190 *
191 * \remarks
192 *     This is a wrapper around KiSwapContextInternal which will save all the
193 *     non-volatile registers so that the Internal function can use all of
194 *     them. It will also save the old current thread and set the new one.
195 *
196 *     The calling thread does not return after KiSwapContextInternal until
197 *     another thread switches to IT.
198 *
199 *--*/
200PUBLIC KiSwapContext
201.PROC KiSwapContext
202
203    /* Generate a KEXCEPTION_FRAME on the stack */
204    GENERATE_EXCEPTION_FRAME
205
206    /* Do the swap with the registers correctly setup */
207    mov r8, gs:[PcCurrentThread] /* Pointer to the new thread */
208    call KiSwapContextInternal
209
210    /* Restore the registers from the KEXCEPTION_FRAME */
211    RESTORE_EXCEPTION_STATE
212
213    /* Return */
214    ret
215.ENDP
216
217END
218