Large partial cleanup of ntoskrnl internal headers, mostly to delete obsoleted or...
[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 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <roscfg.h>
13 #include <internal/i386/ke.h>
14 #include <internal/asm.h>
15 #include <ndk/i386/segment.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, edi
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 #ifdef CONFIG_SMP
133 /* Save FPU state if the thread has used it. */
134 mov dword ptr [ebx+KPCR_NPX_THREAD], 0
135 test byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_DIRTY
136 jz 3f
137 mov eax, [edi+KTHREAD_INITIAL_STACK]
138 cmp dword ptr _FxsrSupport, 0
139 je 1f
140 fxsave [eax-SIZEOF_FX_SAVE_AREA]
141 jmp 2f
142 1:
143 fnsave [eax-SIZEOF_FX_SAVE_AREA]
144 2:
145 mov byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_VALID
146 3:
147 #endif /* CONFIG_SMP */
148
149 /* Save the stack pointer in this processors TSS */
150 mov ebp, [ebx+KPCR_TSS]
151 push ss:[ebp+KTSS_ESP0]
152
153 /* Check if address space switch is needed */
154 mov eax, [esi+KTHREAD_APCSTATE_PROCESS]
155 cmp eax, [edi+KTHREAD_APCSTATE_PROCESS]
156 mov eax, [eax+KPROCESS_DIRECTORY_TABLE_BASE]
157
158 /* Switch stacks */
159 mov [edi+KTHREAD_KERNEL_STACK], esp
160 mov esp, [esi+KTHREAD_KERNEL_STACK]
161
162 jz NoAddressSpaceSwitch
163
164 /* Switch address space */
165 mov cr3, eax
166
167 NoAddressSpaceSwitch:
168
169 /* Stack is OK, safe to enable interrupts now */
170 sti
171
172 /* Check if address space switch is needed (the result from above is valid) */
173 /* If they match, then use the fast-path and skip all this */
174 jz SameProcess
175
176 /* Get the new Process. */
177 mov edi, [esi+KTHREAD_APCSTATE_PROCESS]
178
179 /* Check if we need an LDT */
180 xor eax, eax
181 cmp [edi+KPROCESS_LDT_DESCRIPTOR0], eax
182 jz NoLdt
183
184 /* Write the LDT Selector */
185 mov ebp, [ebx+KPCR_GDT]
186 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR0]
187 mov [ebp+LDT_SELECTOR], eax
188 mov eax, [edi+KPROCESS_LDT_DESCRIPTOR1]
189 mov [ebp+LDT_SELECTOR+4], eax
190
191 /* Save LDT Selector */
192 mov eax, LDT_SELECTOR
193
194 NoLdt:
195
196 /* Load LDT */
197 lldt ax
198
199 /* Get the IOPM */
200 mov ecx, [edi+KPROCESS_IOPM_OFFSET]
201
202 /* Set current IOPM offset in the TSS */
203 mov [ebp+KTSS_IOMAPBASE], cx
204
205 SameProcess:
206
207 /* Set the TEB */
208 mov eax, [esi+KTHREAD_TEB]
209 mov ecx, [ebx+KPCR_GDT]
210 mov [ecx+0x3A], ax
211 shr eax, 16
212 mov [ecx+0x3C], al
213 mov [ecx+0x3F], ah
214
215 /* Increase context switches */
216 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
217
218 /* Set TS in cr0 to catch FPU code and load the FPU state when needed */
219 #ifndef CONFIG_SMP
220 cmp [ebx+KPCR_NPX_THREAD], esi
221 je 4f
222 #endif /* !CONFIG_SMP */
223 mov eax, cr0
224 or eax, X86_CR0_TS
225 mov cr0, eax
226 4:
227
228 /* Restore the stack pointer in this processors TSS */
229 pop ss:[ebp+KTSS_ESP0]
230
231 /* Restore exception list */
232 pop [ebx+KPCR_EXCEPTION_LIST]
233
234 /* Return */
235 call @KeReleaseDispatcherDatabaseLockFromDpcLevel@0
236 ret
237
238 /*++
239 * KiSwapContext
240 *
241 * The KiSwapContext routine switches context to another thread.
242 *
243 * Params:
244 * TargetThread - Pointer to the KTHREAD to which the caller wishes to
245 * switch to.
246 *
247 * Returns:
248 * The WaitStatus of the Target Thread. NOT YET SUPPORTED.
249 *
250 * Remarks:
251 * This is a wrapper around KiSwapContextInternal which will save all the
252 * non-volatile registers so that the Internal function can use all of
253 * them. It will also save the old current thread and set the new one.
254 *
255 * The calling thread does not return after KiSwapContextInternal until
256 * another thread switches to IT.
257 *
258 *--*/
259 .globl @KiSwapContext@4
260 @KiSwapContext@4:
261 /* Note, we CANNOT touch ebp */
262
263 /* Save 4 registers */
264 sub esp, 4 * 4
265
266 /* Save all the non-volatile ones */
267 mov [esp+12], ebx
268 mov [esp+8], esi
269 mov [esp+4], edi
270 mov [esp+0], ebp
271
272 /* Get the Current Thread */
273 mov edi, fs:[KPCR_CURRENT_THREAD]
274
275 /* Get the New Thread */
276 mov esi, ecx
277
278 /* Save it as Current thread */
279 mov fs:[KPCR_CURRENT_THREAD], esi
280
281 /* Do the swap with the registers correctly setup */
282 call @KiSwapContextInternal@0
283
284 /* Return the registers */
285 mov ebp, [esp+0]
286 mov edi, [esp+4]
287 mov esi, [esp+8]
288 mov ebx, [esp+12]
289
290 /* Clean stack */
291 add esp, 4 * 4
292 ret
293
294 #ifdef KDBG
295
296 SaveTrapFrameForKDB:
297 /* Set up a trap frame */
298 pushf // 0x70
299 push cs // 0x6C
300 push 0 /* Error Code */ // 0x64
301 push ebp // 0x60
302 push ebx
303
304 /* Fake Interrupt Stack */
305 mov ebp, [esp+20] /* Eip */
306 mov ebx, [esp+16] /* Eflags */
307 mov [esp+20], ebx
308 mov ebx, [esp+12] /* Cs */
309 mov [esp+16], ebx
310 mov [esp+12], ebp
311
312 push esi
313 push edi
314 push fs
315 push -1 /* Exception List */ // 0x4C
316 push 0 /* Previous Mode */ // 0x48
317 push eax
318 push ecx
319 push edx
320 push ds
321 push es
322 push gs // 0x30
323
324 mov eax, dr7
325 push eax /* Dr7 */
326 /* Clear breakpoint enables in dr7. */
327 and eax, 0xffff0000
328 mov dr7, eax
329 mov eax, dr6
330 push eax /* Dr6 */
331 mov eax, dr3
332 push eax /* Dr3 */
333 mov eax, dr2
334 push eax /* Dr2 */
335 mov eax, dr1
336 push eax /* Dr1 */
337 mov eax, dr0
338 push eax /* Dr0 */
339
340 lea eax, [esp+0x58]
341 push eax /* TempEsp */
342 push ss /* TempSegSs */
343 push 0 /* DebugPointer */
344 push -1 /* DebugArgMark */
345 push [esp+60] /* Debug EIP */ // 0x4
346 push ebp /* Debug EBP */ // 0x0
347
348 /* Set Stack */
349 mov ebp, esp
350
351 /* Push old Trap Frame */
352 push [edi+KTHREAD_TRAP_FRAME]
353
354 /* Save new one */
355 mov [edi+KTHREAD_TRAP_FRAME], ebp
356
357 /* Restore EBP, EBX and EAX */
358 mov ebp, [ebp+KTRAP_FRAME_EBP]
359 mov ebx, [ebp+KTRAP_FRAME_EBX]
360 mov eax, [ebp+KTRAP_FRAME_EAX]
361
362 /* Return EIP */
363 push offset RestoreTrapFrameForKDB
364
365 /* Jump to normal code */
366 jmp SaveTrapFrameForKDB_Return
367
368 RestoreTrapFrameForKDB:
369
370 /* Restore the old trapframe */
371 pop [esi+KTHREAD_TRAP_FRAME]
372
373 /* Pop unused portions of the trap frame */
374 add esp, 0x30
375
376 /* Restore registers from Trap frame */
377 pop gs
378 pop es
379 pop ds
380 pop edx
381 pop ecx
382 pop eax
383 add esp, 8 /* ExceptionList and PreviousMode */
384 pop fs
385 pop edi
386 pop esi
387 pop ebx
388 pop ebp
389 add esp, 4 /* ErrorCode */
390
391 /* Return to the caller. */
392 iret
393 #endif /* KDBG */
394
395