f8de2f05c1eed153affaab7efc16b0d94a238d8e
[reactos.git] / reactos / ntoskrnl / ke / i386 / ctxswitch.S
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/i386/ctxswitch.S
5 * PURPOSE: Thread Context Switching
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * Gregor Anich (FPU Code)
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <roscfg.h>
14 #include <internal/i386/ke.h>
15 #include <ndk/asm.h>
16 .intel_syntax noprefix
17
18 #define Running 2
19 #define SIZEOF_TRAP_FRAME 0x8c
20 #define APC_LEVEL 1
21
22 /* GLOBALS ****************************************************************/
23
24 /* FUNCTIONS ****************************************************************/
25
26 /*++
27 * KiThreadStartup
28 *
29 * The KiThreadStartup routine is the beginning of any thread.
30 *
31 * Params:
32 * SystemRoutine - Pointer to the System Startup Routine. Either
33 * PspUserThreadStartup or PspSystemThreadStartup
34 *
35 * StartRoutine - For Kernel Threads only, specifies the starting execution
36 * point of the new thread.
37 *
38 * StartContext - For Kernel Threads only, specifies a pointer to variable
39 * context data to be sent to the StartRoutine above.
40 *
41 * UserThread - Indicates whether or not this is a user thread. This tells
42 * us if the thread has a context or not.
43 *
44 * TrapFrame - Pointer to the KTHREAD to which the caller wishes to
45 * switch from.
46 *
47 * Returns:
48 * Should never return for a system thread. Returns through the System Call
49 * Exit Dispatcher for a user thread.
50 *
51 * Remarks:
52 * If a return from a system thread is detected, a bug check will occur.
53 *
54 *--*/
55 .globl _KiThreadStartup@156
56 _KiThreadStartup@156:
57
58 /*
59 * Clear all the non-volatile registers, so the thread won't be tempted to
60 * expect any static data (like some badly coded usermode/win9x apps do)
61 */
62 xor ebx, ebx
63 xor esi, esi
64 xor edi, edi
65 xor ebp, ebp
66
67 /* It's now safe to go to APC */
68 mov ecx, APC_LEVEL
69 call @KfLowerIrql@4
70
71 /*
72 * Call the System Routine which is right on our stack now.
73 * After we pop the pointer, the Start Routine/Context will be on the
74 * stack, as parameters to the System Routine
75 */
76 pop eax
77 call eax
78
79 /* The thread returned... was it a user-thread? */
80 pop ecx
81 or ecx, ecx
82 jz BadThread
83
84 /* Yes it was, set our trapframe for the System Call Exit Dispatcher */
85 mov ebp, esp
86
87 /* Exit back to user-mode */
88 jmp _KiServiceExit2
89
90 BadThread:
91
92 /* A system thread returned...this is very bad! */
93 int 3
94
95 /*++
96 * KiSwapContextInternal
97 *
98 * The KiSwapContextInternal routine switches context to another thread.
99 *
100 * Params:
101 * ESI - Pointer to the KTHREAD to which the caller wishes to
102 * switch to.
103 * EDI - Pointer to the KTHREAD to which the caller wishes to
104 * switch from.
105 *
106 * Returns:
107 * None.
108 *
109 * Remarks:
110 * Absolutely all registers except ESP can be trampled here for maximum code flexibility.
111 *
112 *--*/
113 .globl @KiSwapContextInternal@0
114 @KiSwapContextInternal@0:
115 #ifdef KDBG
116 //jmp SaveTrapFrameForKDB
117 SaveTrapFrameForKDB_Return:
118 #endif
119
120 /* Get the PCR. It's faster to use ebx+offset then fs:offset */
121 mov ebx, [fs:KPCR_SELF]
122
123 /* Set the Thread to running */
124 mov byte ptr [esi+KTHREAD_STATE], Running
125
126 /* Save the Exception list */
127 push [ebx+KPCR_EXCEPTION_LIST]
128
129 /* Switching, disable interrupts now */
130 cli
131
132 /* Save the initial stack in EAX */
133 mov eax, [edi+KTHREAD_INITIAL_STACK]
134
135 #ifdef CONFIG_SMP
136 /* Save FPU state if the thread has used it. */
137 mov dword ptr [ebx+KPCR_NPX_THREAD], 0
138 test byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_DIRTY
139 jz 3f
140 cmp dword ptr _FxsrSupport, 0
141 je 1f
142 fxsave [eax-SIZEOF_FX_SAVE_AREA]
143 jmp 2f
144 1:
145 fnsave [eax-SIZEOF_FX_SAVE_AREA]
146 2:
147 mov byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_VALID
148 3:
149 #endif /* CONFIG_SMP */
150
151 /* Save the stack pointer in this processors TSS */
152 mov ebp, [ebx+KPCR_TSS]
153
154 /* Check if this isn't V86 Mode, so we can bias the Esp0 */
155 test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS], X86_EFLAGS_VM
156 jnz NoAdjust
157
158 /* Bias esp */
159 //sub dword ptr ss:[ebp+KTSS_ESP0], KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
160
161 NoAdjust:
162 /* Push ESP0 Value */
163 push ss:[ebp+KTSS_ESP0]
164
165 /* Check if address space switch is needed */
166 mov eax, [esi+KTHREAD_APCSTATE_PROCESS]
167 cmp eax, [edi+KTHREAD_APCSTATE_PROCESS]
168 mov eax, [eax+KPROCESS_DIRECTORY_TABLE_BASE]
169
170 /* Switch stacks */
171 mov [edi+KTHREAD_KERNEL_STACK], esp
172 mov esp, [esi+KTHREAD_KERNEL_STACK]
173
174 jz NoAddressSpaceSwitch
175
176 /* Switch address space */
177 mov cr3, eax
178
179 NoAddressSpaceSwitch:
180
181 /* Stack is OK, safe to enable interrupts now */
182 sti
183
184 /* Check if address space switch is needed (the result from above is valid) */
185 /* If they match, then use the fast-path and skip all this */
186 jz SameProcess
187
188 /* Get the new Process. */
189 mov edi, [esi+KTHREAD_APCSTATE_PROCESS]
190
191 /* Check if we need an LDT */
192 xor eax, eax
193 cmp [edi+KPROCESS_LDT_DESCRIPTOR0], eax
194 jz NoLdt
195
196 /* Write the LDT Selector */
197 mov ebp, [ebx+KPCR_GDT]
198 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR0]
199 mov [ebp+KGDT_LDT], eax
200 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR1]
201 mov [ebp+KGDT_LDT+4], eax
202
203 /* Save LDT Selector */
204 mov eax, KGDT_LDT
205
206 NoLdt:
207
208 /* Load LDT */
209 lldt ax
210
211 /* Get the IOPM */
212 mov ecx, [edi+KPROCESS_IOPM_OFFSET]
213
214 /* Set current IOPM offset in the TSS */
215 mov [ebp+KTSS_IOMAPBASE], cx
216
217 SameProcess:
218
219 /* Set the TEB */
220 mov eax, [esi+KTHREAD_TEB]
221 mov ecx, [ebx+KPCR_GDT]
222 mov [ecx+0x3A], ax
223 shr eax, 16
224 mov [ecx+0x3C], al
225 mov [ecx+0x3F], ah
226
227 /* Increase context switches */
228 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
229
230 /* Set TS in cr0 to catch FPU code and load the FPU state when needed */
231 #ifndef CONFIG_SMP
232 cmp [ebx+KPCR_NPX_THREAD], esi
233 je 4f
234 #endif /* !CONFIG_SMP */
235 mov eax, cr0
236 or eax, X86_CR0_TS
237 mov cr0, eax
238 4:
239
240 /* Restore the stack pointer in this processors TSS */
241 pop ss:[ebp+KTSS_ESP0]
242
243 /* Restore exception list */
244 pop [ebx+KPCR_EXCEPTION_LIST]
245
246 /* Return */
247 call @KeReleaseDispatcherDatabaseLockFromDpcLevel@0
248 ret
249
250 /*++
251 * KiSwapContext
252 *
253 * The KiSwapContext routine switches context to another thread.
254 *
255 * Params:
256 * TargetThread - Pointer to the KTHREAD to which the caller wishes to
257 * switch to.
258 *
259 * Returns:
260 * The WaitStatus of the Target Thread. NOT YET SUPPORTED.
261 *
262 * Remarks:
263 * This is a wrapper around KiSwapContextInternal which will save all the
264 * non-volatile registers so that the Internal function can use all of
265 * them. It will also save the old current thread and set the new one.
266 *
267 * The calling thread does not return after KiSwapContextInternal until
268 * another thread switches to IT.
269 *
270 *--*/
271 .globl @KiSwapContext@4
272 @KiSwapContext@4:
273 /* Note, we CANNOT touch ebp */
274
275 /* Save 4 registers */
276 sub esp, 4 * 4
277
278 /* Save all the non-volatile ones */
279 mov [esp+12], ebx
280 mov [esp+8], esi
281 mov [esp+4], edi
282 mov [esp+0], ebp
283
284 /* Get the Current Thread */
285 mov edi, fs:[KPCR_CURRENT_THREAD]
286
287 /* Get the New Thread */
288 mov esi, ecx
289
290 /* Save it as Current thread */
291 mov fs:[KPCR_CURRENT_THREAD], esi
292
293 /* Do the swap with the registers correctly setup */
294 call @KiSwapContextInternal@0
295
296 /* Return the registers */
297 mov ebp, [esp+0]
298 mov edi, [esp+4]
299 mov esi, [esp+8]
300 mov ebx, [esp+12]
301
302 /* Clean stack */
303 add esp, 4 * 4
304 ret
305
306 #ifdef KDBG
307
308 SaveTrapFrameForKDB:
309 /* Set up a trap frame */
310 pushf // 0x70
311 push cs // 0x6C
312 push 0 /* Error Code */ // 0x64
313 push ebp // 0x60
314 push ebx
315
316 /* Fake Interrupt Stack */
317 mov ebp, [esp+20] /* Eip */
318 mov ebx, [esp+16] /* Eflags */
319 mov [esp+20], ebx
320 mov ebx, [esp+12] /* Cs */
321 mov [esp+16], ebx
322 mov [esp+12], ebp
323
324 push esi
325 push edi
326 push fs
327 push -1 /* Exception List */ // 0x4C
328 push 0 /* Previous Mode */ // 0x48
329 push eax
330 push ecx
331 push edx
332 push ds
333 push es
334 push gs // 0x30
335
336 mov eax, dr7
337 push eax /* Dr7 */
338 /* Clear breakpoint enables in dr7. */
339 and eax, 0xffff0000
340 mov dr7, eax
341 mov eax, dr6
342 push eax /* Dr6 */
343 mov eax, dr3
344 push eax /* Dr3 */
345 mov eax, dr2
346 push eax /* Dr2 */
347 mov eax, dr1
348 push eax /* Dr1 */
349 mov eax, dr0
350 push eax /* Dr0 */
351
352 lea eax, [esp+0x58]
353 push eax /* TempEsp */
354 push ss /* TempSegSs */
355 push 0 /* DebugPointer */
356 push -1 /* DebugArgMark */
357 push [esp+60] /* Debug EIP */ // 0x4
358 push ebp /* Debug EBP */ // 0x0
359
360 /* Set Stack */
361 mov ebp, esp
362
363 /* Push old Trap Frame */
364 push [edi+KTHREAD_TRAP_FRAME]
365
366 /* Save new one */
367 mov [edi+KTHREAD_TRAP_FRAME], ebp
368
369 /* Restore EBP, EBX and EAX */
370 mov ebp, [ebp+KTRAP_FRAME_EBP]
371 mov ebx, [ebp+KTRAP_FRAME_EBX]
372 mov eax, [ebp+KTRAP_FRAME_EAX]
373
374 /* Return EIP */
375 push offset RestoreTrapFrameForKDB
376
377 /* Jump to normal code */
378 jmp SaveTrapFrameForKDB_Return
379
380 RestoreTrapFrameForKDB:
381
382 /* Restore the old trapframe */
383 pop [esi+KTHREAD_TRAP_FRAME]
384
385 /* Pop unused portions of the trap frame */
386 add esp, 0x30
387
388 /* Restore registers from Trap frame */
389 pop gs
390 pop es
391 pop ds
392 pop edx
393 pop ecx
394 pop eax
395 add esp, 8 /* ExceptionList and PreviousMode */
396 pop fs
397 pop edi
398 pop esi
399 pop ebx
400 pop ebp
401 add esp, 4 /* ErrorCode */
402
403 /* Return to the caller. */
404 iret
405 #endif /* KDBG */
406
407