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
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * Gregor Anich (FPU Code)
11 /* INCLUDES ******************************************************************/
14 .intel_syntax noprefix
18 #define WrDispatchInt 0x1F
20 Dividend: .float 4195835.0
21 Divisor: .float 3145727.0
25 /* FUNCTIONS ****************************************************************/
27 .globl _KiIsNpxErrataPresent@0
28 .func KiIsNpxErrataPresent@0
29 _KiIsNpxErrataPresent@0:
31 /* Disable interrupts */
34 /* Get CR0 and mask out FPU flags */
37 and eax, ~(CR0_MP + CR0_TS + CR0_EM)
40 /* Initialize the FPU */
43 /* Do the divison and inverse multiplication */
44 fld qword ptr Dividend
45 fstp qword ptr Result1
47 fstp qword ptr Result2
49 fdiv qword ptr Result2
50 fmul qword ptr Result2
52 /* Do the compare and check flags */
53 fcomp qword ptr Result1
57 /* Restore CR0 and interrupts */
61 /* Return errata status */
70 .globl _KiIsNpxPresent@0
71 .func KiIsNpxPresent@0
77 /* Get CR0 and mask out FPU flags */
79 and eax, ~(CR0_MP + CR0_TS + CR0_EM + CR0_ET)
81 /* Initialize the FPU and assume FALSE for return */
85 /* Save magic value on stack */
89 /* Setup stack for FPU store */
93 /* Now check if our magic got cleared */
97 /* Enable FPU, set return to TRUE */
101 /* If this is a 486 or higher, enable INT 16 as well */
102 cmp dword ptr fs:KPCR_PRCB_CPU_TYPE, 3
107 /* Set emulation enabled during the first boot phase and set the CR0 */
108 or eax, (CR0_EM + CR0_TS)
115 /* Return true or false */
120 .globl _KiFlushNPXState@4
121 .func KiFlushNPXState@4
124 /* Save volatiles and disable interrupts */
131 /* Save the PCR and get the current thread */
132 mov edi, fs:[KPCR_SELF]
133 mov esi, [edi+KPCR_CURRENT_THREAD]
135 /* Check if we're already loaded */
136 cmp byte ptr [esi+KTHREAD_NPX_STATE], NPX_STATE_LOADED
139 /* Check if we're supposed to get it */
140 cmp dword ptr [esp+20], 0
144 /* Assert Fxsr support */
145 test byte ptr _KeI386FxsrPresent, 1
151 /* Get CR0 and test if it's valid */
153 test bl, CR0_MP + CR0_TS + CR0_EM
156 /* Enable fnsave to work */
157 and ebx, ~(CR0_MP + CR0_TS + CR0_EM)
161 /* Check if we are the NPX Thread */
162 mov eax, [edi+KPCR_NPX_THREAD]
166 /* Check if it's not loaded */
167 cmp byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
171 /* We are the NPX Thread with an unloaded NPX State... this isn't normal! */
175 /* Save the NPX State */
176 mov ecx, [eax+KTHREAD_INITIAL_STACK]
177 sub ecx, NPX_FRAME_LENGTH
179 mov byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
182 /* Load the NPX State */
183 mov ecx, [esi+KTHREAD_INITIAL_STACK]
184 sub ecx, NPX_FRAME_LENGTH
187 /* Get the CR0 state and destination */
188 mov edx, [ecx+FN_CR0_NPX_STATE]
193 /* We already have a valid state, flush it */
195 test bl, CR0_MP + CR0_TS + CR0_EM
198 /* Enable fnsave to work */
199 and ebx, ~(CR0_MP + CR0_TS + CR0_EM)
203 /* Get the kernel stack */
204 mov ecx, [esi+KTHREAD_INITIAL_STACK]
205 test byte ptr _KeI386FxsrPresent, 1
206 lea ecx, [ecx-NPX_FRAME_LENGTH]
208 /* Set the NPX State */
209 mov byte ptr [esi+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
212 mov edx, [ecx+FN_CR0_NPX_STATE]
215 /* Save the FX State */
218 /* Check if we also have to save it in the parameter */
223 /* Save the Fn state in the parameter we got */
232 or ebx, NPX_STATE_NOT_LOADED
234 /* Clear the NPX thread */
235 mov [edi+KPCR_NPX_THREAD], eax
237 /* Add saved CR0 into NPX State, and set it */
241 /* Re-enable interrupts and return */
254 * The KiThreadStartup routine is the beginning of any thread.
257 * SystemRoutine - Pointer to the System Startup Routine. Either
258 * PspUserThreadStartup or PspSystemThreadStartup
260 * StartRoutine - For Kernel Threads only, specifies the starting execution
261 * point of the new thread.
263 * StartContext - For Kernel Threads only, specifies a pointer to variable
264 * context data to be sent to the StartRoutine above.
266 * UserThread - Indicates whether or not this is a user thread. This tells
267 * us if the thread has a context or not.
269 * TrapFrame - Pointer to the KTHREAD to which the caller wishes to
273 * Should never return for a system thread. Returns through the System Call
274 * Exit Dispatcher for a user thread.
277 * If a return from a system thread is detected, a bug check will occur.
280 .func KiThreadStartup@156
281 .globl _KiThreadStartup@156
282 _KiThreadStartup@156:
285 * Clear all the non-volatile registers, so the thread won't be tempted to
286 * expect any static data (like some badly coded usermode/win9x apps do)
293 /* It's now safe to go to APC */
298 * Call the System Routine which is right on our stack now.
299 * After we pop the pointer, the Start Routine/Context will be on the
300 * stack, as parameters to the System Routine
305 /* The thread returned... was it a user-thread? */
310 /* Yes it was, set our trapframe for the System Call Exit Dispatcher */
313 /* Exit back to user-mode */
318 /* A system thread returned...this is very bad! */
323 * KiSwapContextInternal
326 * The KiSwapContextInternal routine switches context to another thread.
328 * BOOLEAN USERCALL KiSwapContextInternal();
331 * ESI - Pointer to the KTHREAD to which the caller wishes to
333 * EDI - Pointer to the KTHREAD to which the caller wishes to
340 * Absolutely all registers except ESP can be trampled here for maximum code flexibility.
343 .globl @KiSwapContextInternal@0
344 .func @KiSwapContextInternal@0, @KiSwapContextInternal@0
345 @KiSwapContextInternal@0:
352 /* Acquire the swap lock */
353 cmp byte ptr [esi+KTHREAD_SWAP_BUSY], 0
359 /* Increase context switches (use ES for lazy load) */
360 inc dword ptr es:[ebx+KPCR_CONTEXT_SWITCHES]
362 /* Save the Exception list */
363 push [ebx+KPCR_EXCEPTION_LIST]
366 cmp dword ptr [ebx+KPCR_PERF_GLOBAL_GROUP_MASK], 0
372 /* Assert that we're on the right CPU */
373 mov cl, [esi+KTHREAD_NEXT_PROCESSOR]
374 cmp cl, [ebx+KPCR_PROCESSOR_NUMBER]
379 /* Get CR0 and save it */
384 /* Check NPX State */
385 cmp byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_LOADED
391 mov [edi+KTHREAD_KERNEL_STACK], esp
393 /* Checking NPX, disable interrupts now */
394 mov eax, [esi+KTHREAD_INITIAL_STACK]
400 /* Get the NPX State */
401 movzx ecx, byte ptr [esi+KTHREAD_NPX_STATE]
403 /* Clear the other bits, merge in CR0, merge in FPU CR0 bits and compare */
404 and edx, ~(CR0_MP + CR0_EM + CR0_TS)
406 or ecx, [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)]
411 /* Enable interrupts and set the current stack */
413 mov esp, [esi+KTHREAD_KERNEL_STACK]
415 /* Check if address space switch is needed */
416 mov ebp, [esi+KTHREAD_APCSTATE_PROCESS]
417 mov eax, [edi+KTHREAD_APCSTATE_PROCESS]
422 /* Get the active processors and XOR with the process' */
423 mov ecx, [ebx+KPCR_SET_MEMBER_COPY]
424 lock xor [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
425 lock xor [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
427 /* Assert change went ok */
429 test [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
431 test [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
436 /* Check if we need an LDT */
437 mov ecx, [ebp+KPROCESS_LDT_DESCRIPTOR0]
438 or ecx, [eax+KPROCESS_LDT_DESCRIPTOR0]
442 /* Switch address space */
443 mov eax, [ebp+KPROCESS_DIRECTORY_TABLE_BASE]
449 /* Release swap lock */
450 and byte ptr [edi+KTHREAD_SWAP_BUSY], 0
458 mov eax, [esi+KTHREAD_TEB]
459 mov [ebx+KPCR_TEB], eax
460 mov ecx, [ebx+KPCR_GDT]
466 /* Get stack pointer */
467 mov eax, [esi+KTHREAD_INITIAL_STACK]
469 /* Make space for the NPX Frame */
470 sub eax, NPX_FRAME_LENGTH
472 /* Check if this isn't V86 Mode, so we can bias the Esp0 */
473 test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
477 sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
482 mov ecx, [ebx+KPCR_TSS]
483 mov [ecx+KTSS_ESP0], eax
485 /* Set current IOPM offset in the TSS */
486 mov ax, [ebp+KPROCESS_IOPM_OFFSET]
487 mov [ecx+KTSS_IOMAPBASE], ax
489 /* Increase context switches */
490 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
492 /* Restore exception list */
493 pop [ebx+KPCR_EXCEPTION_LIST]
498 /* DPC shouldn't be active */
499 cmp byte ptr [ebx+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
502 /* Check if kernel APCs are pending */
503 cmp byte ptr [esi+KTHREAD_PENDING_KERNEL_APC], 0
506 /* No APCs, return */
512 /* Check if they're disabled */
513 cmp word ptr [esi+KTHREAD_SPECIAL_APC_DISABLE], 0
518 /* Request APC Delivery */
520 call @HalRequestSoftwareInterrupt@4
525 /* Return with APC pending */
530 /* Check if it's empty */
531 mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR0]
535 /* Write the LDT Selector */
536 mov ecx, [ebx+KPCR_GDT]
537 mov [ecx+KGDT_LDT], eax
538 mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR1]
539 mov [ecx+KGDT_LDT+4], eax
541 /* Write the INT21 handler */
542 mov ecx, [ebx+KPCR_IDT]
543 mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR0]
545 mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR1]
548 /* Save LDT Selector */
558 /* Assert NPX State */
559 test byte ptr [esi+KTHREAD_NPX_STATE], ~(NPX_STATE_NOT_LOADED)
561 test dword ptr [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)], ~(CR0_PE + CR0_MP + CR0_EM + CR0_TS)
581 /* No WMI support yet */
589 /* Bugcheck the machine, printing out the threads being switched */
590 mov eax, [edi+KTHREAD_INITIAL_STACK]
595 push ATTEMPTED_SWITCH_FROM_DPC
596 call _KeBugCheckEx@20
612 * The KiSwapContext routine switches context to another thread.
615 * KiSwapContext(PKTHREAD CurrentThread, PKTHREAD TargetThread);
617 * \param CurrentThread
618 * Pointer to the KTHREAD of the current thread.
620 * \param TargetThread
621 * Pointer to the KTHREAD to which the caller wishes to switch to.
624 * The WaitStatus of the Target Thread.
627 * This is a wrapper around KiSwapContextInternal which will save all the
628 * non-volatile registers so that the Internal function can use all of
629 * them. It will also save the old current thread and set the new one.
631 * The calling thread does not return after KiSwapContextInternal until
632 * another thread switches to IT.
635 .globl @KiSwapContext@8
636 .func @KiSwapContext@8, @KiSwapContext@8
639 /* Save 4 registers */
642 /* Save all the non-volatile ones */
648 /* Get the current KPCR */
649 mov ebx, fs:[KPCR_SELF]
651 /* Get the Current Thread */
654 /* Get the New Thread */
657 /* Get the wait IRQL */
658 movzx ecx, byte ptr [edi+KTHREAD_WAIT_IRQL]
660 /* Do the swap with the registers correctly setup */
661 call @KiSwapContextInternal@0
663 /* Return the registers */
675 .func @KiIdleLoop@0, @KiIdleLoop@0
679 mov ebx, fs:[KPCR_SELF]
681 /* Jump into mainline code */
685 /* Call the CPU's idle function */
686 lea ecx, [ebx+KPCR_PRCB_POWER_STATE_IDLE_FUNCTION]
690 /* Cycle interrupts for 1 cycle */
696 /* Check if we have to deliver DPCs, timers, or deferred threads */
697 mov eax, [ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]
698 or eax, [ebx+KPCR_PRCB_TIMER_REQUEST]
700 or eax, [ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]
704 mov cl, DISPATCH_LEVEL
705 call @HalClearSoftwareInterrupt@4
707 /* Handle the above */
708 lea ecx, [ebx+KPCR_PRCB_DATA]
709 call @KiRetireDpcList@4
712 /* Check if a next thread is queued */
713 cmp dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
721 /* There is, raise IRQL to synch level */
722 call _KeRaiseIrqlToSynchLevel@0
726 /* Set the current thread to ready */
727 mov edi, [ebx+KPCR_CURRENT_THREAD]
729 mov byte ptr [edi+KTHREAD_SWAP_BUSY], 1
731 /* Acquire the PRCB Lock */
732 lock bts dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
734 lea ecx, [ebx+KPCR_PRCB_PRCB_LOCK]
735 call @KefAcquireSpinLockAtDpcLevel@4
739 /* Check if the next thread is the current */
740 mov esi, [ebx+KPCR_PRCB_NEXT_THREAD]
746 /* Clear the next thread and set this one instead */
747 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
748 mov [ebx+KPCR_CURRENT_THREAD], esi
750 /* Set the thread as running */
751 mov byte ptr [esi+KTHREAD_STATE_], Running
754 /* Disable the idle scheduler and release the PRCB lock */
755 and byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
756 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
760 /* ReactOS Mm Hack */
762 call @MiSyncForContextSwitch@4
764 /* Swap context at APC_LEVEL */
766 call @KiSwapContextInternal@0
769 /* Lower to DPC level */
770 mov ecx, DISPATCH_LEVEL
777 /* Clear the next thread, and put the thready as ready after lock release */
778 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
779 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
780 and byte ptr [edi+KTHREAD_STATE_], Ready
784 /* Check if the idle scheduler is enabled */
785 cmp byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
788 /* It is, so call the scheduler */
789 lea ecx, [ebx+KPCR_PRCB_DATA]
790 call @KiIdleSchedule@4
793 /* Get new thread pointers and either swap or idle loop again */
795 mov edi, [ebx+KPCR_PRCB_IDLE_THREAD]
801 .globl _Ki386AdjustEsp0@4
802 .func Ki386AdjustEsp0@4
805 /* Get the current thread */
806 mov eax, [fs:KPCR_CURRENT_THREAD]
808 /* Get trap frame and stack */
810 mov eax, [eax+KTHREAD_INITIAL_STACK]
813 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
817 sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
820 /* Skip FX Save Area */
821 sub eax, SIZEOF_FX_SAVE_AREA
823 /* Disable interrupts */
828 mov edx, [fs:KPCR_TSS]
829 mov ss:[edx+KTSS_ESP0], eax
831 /* Enable interrupts and return */
836 .globl _KiSwapProcess@8
837 .func KiSwapProcess@8
840 /* Get process pointers */
845 /* Update active processors */
846 mov ecx, fs:[KPCR_SET_MEMBER]
847 lock xor [edx+KPROCESS_ACTIVE_PROCESSORS], ecx
848 lock xor [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
852 test dword ptr [edx+KPROCESS_ACTIVE_PROCESSORS], 0
854 test dword ptr [eax+KPROCESS_ACTIVE_PROCESSORS], 0
859 /* Check if their LDTs changed */
860 mov ecx, [edx+KPROCESS_LDT_DESCRIPTOR0]
861 or ecx, [eax+KPROCESS_LDT_DESCRIPTOR0]
865 mov eax, [edx+KPROCESS_DIRECTORY_TABLE_BASE]
869 mov ecx, fs:[KPCR_TSS]
871 /* Clear GS on process swap */
875 /* Update IOPM offset */
876 mov ax, [edx+KPROCESS_IOPM_OFFSET]
877 mov [ecx+KTSS_IOMAPBASE], ax