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