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