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