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]
397 /* Get the NPX State */
398 movzx ecx, byte ptr [esi+KTHREAD_NPX_STATE]
400 /* Clear the other bits, merge in CR0, merge in FPU CR0 bits and compare */
401 and edx, ~(CR0_MP + CR0_EM + CR0_TS)
403 or ecx, [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)]
408 /* Enable interrupts and set the current stack */
410 mov esp, [esi+KTHREAD_KERNEL_STACK]
412 /* Check if address space switch is needed */
413 mov ebp, [esi+KTHREAD_APCSTATE_PROCESS]
414 mov eax, [edi+KTHREAD_APCSTATE_PROCESS]
419 /* Get the active processors and XOR with the process' */
420 mov ecx, [ebx+KPCR_SET_MEMBER_COPY]
421 lock xor [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
422 lock xor [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
424 /* Assert change went ok */
426 test [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
428 test [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
433 /* Check if we need an LDT */
434 mov ecx, [ebp+KPROCESS_LDT_DESCRIPTOR0]
435 or ecx, [eax+KPROCESS_LDT_DESCRIPTOR0]
439 /* Switch address space */
440 mov eax, [ebp+KPROCESS_DIRECTORY_TABLE_BASE]
446 /* Release swap lock */
447 and byte ptr [edi+KTHREAD_SWAP_BUSY], 0
455 mov eax, [esi+KTHREAD_TEB]
456 mov [ebx+KPCR_TEB], eax
457 mov ecx, [ebx+KPCR_GDT]
463 /* Get stack pointer */
464 mov eax, [esi+KTHREAD_INITIAL_STACK]
466 /* Make space for the NPX Frame */
467 sub eax, NPX_FRAME_LENGTH
469 /* Check if this isn't V86 Mode, so we can bias the Esp0 */
470 test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
474 sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
479 mov ecx, [ebx+KPCR_TSS]
480 mov [ecx+KTSS_ESP0], eax
482 /* Set current IOPM offset in the TSS */
483 mov ax, [ebp+KPROCESS_IOPM_OFFSET]
484 mov [ecx+KTSS_IOMAPBASE], ax
486 /* Increase context switches */
487 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
489 /* Restore exception list */
490 pop [ebx+KPCR_EXCEPTION_LIST]
495 /* DPC shouldn't be active */
496 cmp byte ptr [ebx+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
499 /* Check if kernel APCs are pending */
500 cmp byte ptr [esi+KTHREAD_PENDING_KERNEL_APC], 0
503 /* No APCs, return */
509 /* Check if they're disabled */
510 cmp word ptr [esi+KTHREAD_SPECIAL_APC_DISABLE], 0
515 /* Request APC Delivery */
517 call @HalRequestSoftwareInterrupt@4
522 /* Return with APC pending */
527 /* Check if it's empty */
528 mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR0]
532 /* Write the LDT Selector */
533 mov ecx, [ebx+KPCR_GDT]
534 mov [ecx+KGDT_LDT], eax
535 mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR1]
536 mov [ecx+KGDT_LDT+4], eax
538 /* Write the INT21 handler */
539 mov ecx, [ebx+KPCR_IDT]
540 mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR0]
542 mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR1]
545 /* Save LDT Selector */
555 /* Assert NPX State */
556 test byte ptr [esi+KTHREAD_NPX_STATE], ~(NPX_STATE_NOT_LOADED)
558 test dword ptr [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)], ~(CR0_PE + CR0_MP + CR0_EM + CR0_TS)
578 /* No WMI support yet */
586 /* Bugcheck the machine, printing out the threads being switched */
587 mov eax, [edi+KTHREAD_INITIAL_STACK]
592 push ATTEMPTED_SWITCH_FROM_DPC
593 call _KeBugCheckEx@20
609 * The KiSwapContext routine switches context to another thread.
612 * KiSwapContext(PKTHREAD CurrentThread, PKTHREAD TargetThread);
614 * \param CurrentThread
615 * Pointer to the KTHREAD of the current thread.
617 * \param TargetThread
618 * Pointer to the KTHREAD to which the caller wishes to switch to.
621 * The WaitStatus of the Target Thread.
624 * This is a wrapper around KiSwapContextInternal which will save all the
625 * non-volatile registers so that the Internal function can use all of
626 * them. It will also save the old current thread and set the new one.
628 * The calling thread does not return after KiSwapContextInternal until
629 * another thread switches to IT.
632 .globl @KiSwapContext@8
633 .func @KiSwapContext@8, @KiSwapContext@8
636 /* Save 4 registers */
639 /* Save all the non-volatile ones */
645 /* Get the current KPCR */
646 mov ebx, fs:[KPCR_SELF]
648 /* Get the Current Thread */
651 /* Get the New Thread */
654 /* Get the wait IRQL */
655 movzx ecx, byte ptr [edi+KTHREAD_WAIT_IRQL]
657 /* Do the swap with the registers correctly setup */
658 call @KiSwapContextInternal@0
660 /* Return the registers */
672 .func @KiIdleLoop@0, @KiIdleLoop@0
676 mov ebx, fs:[KPCR_SELF]
678 /* Jump into mainline code */
682 /* Call the CPU's idle function */
683 lea ecx, [ebx+KPCR_PRCB_POWER_STATE_IDLE_FUNCTION]
687 /* Cycle interrupts for 1 cycle */
693 /* Check if we have to deliver DPCs, timers, or deferred threads */
694 mov eax, [ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]
695 or eax, [ebx+KPCR_PRCB_TIMER_REQUEST]
697 or eax, [ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]
701 mov cl, DISPATCH_LEVEL
702 call @HalClearSoftwareInterrupt@4
704 /* Handle the above */
705 lea ecx, [ebx+KPCR_PRCB_DATA]
706 call @KiRetireDpcList@4
709 /* Check if a next thread is queued */
710 cmp dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
718 /* There is, raise IRQL to synch level */
719 call _KeRaiseIrqlToSynchLevel@0
723 /* Set the current thread to ready */
724 mov edi, [ebx+KPCR_CURRENT_THREAD]
726 mov byte ptr [edi+KTHREAD_SWAP_BUSY], 1
728 /* Acquire the PRCB Lock */
729 lock bts dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
731 lea ecx, [ebx+KPCR_PRCB_PRCB_LOCK]
732 call @KefAcquireSpinLockAtDpcLevel@4
736 /* Check if the next thread is the current */
737 mov esi, [ebx+KPCR_PRCB_NEXT_THREAD]
743 /* Clear the next thread and set this one instead */
744 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
745 mov [ebx+KPCR_CURRENT_THREAD], esi
747 /* Set the thread as running */
748 mov byte ptr [esi+KTHREAD_STATE_], Running
751 /* Disable the idle scheduler and release the PRCB lock */
752 and byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
753 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
757 /* ReactOS Mm Hack */
759 call @MiSyncForContextSwitch@4
761 /* Swap context at APC_LEVEL */
763 call @KiSwapContextInternal@0
766 /* Lower to DPC level */
767 mov ecx, DISPATCH_LEVEL
774 /* Clear the next thread, and put the thready as ready after lock release */
775 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
776 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
777 and byte ptr [edi+KTHREAD_STATE_], Ready
781 /* Check if the idle scheduler is enabled */
782 cmp byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
785 /* It is, so call the scheduler */
786 lea ecx, [ebx+KPCR_PRCB_DATA]
787 call @KiIdleSchedule@4
790 /* Get new thread pointers and either swap or idle loop again */
792 mov edi, [ebx+KPCR_PRCB_IDLE_THREAD]
798 .globl _Ki386AdjustEsp0@4
799 .func Ki386AdjustEsp0@4
802 /* Get the current thread */
803 mov eax, [fs:KPCR_CURRENT_THREAD]
805 /* Get trap frame and stack */
807 mov eax, [eax+KTHREAD_INITIAL_STACK]
810 test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
814 sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
817 /* Skip FX Save Area */
818 sub eax, SIZEOF_FX_SAVE_AREA
820 /* Disable interrupts */
825 mov edx, [fs:KPCR_TSS]
826 mov ss:[edx+KTSS_ESP0], eax
828 /* Enable interrupts and return */
833 .globl _KiSwapProcess@8
834 .func KiSwapProcess@8
837 /* Get process pointers */
842 /* Update active processors */
843 mov ecx, fs:[KPCR_SET_MEMBER]
844 lock xor [edx+KPROCESS_ACTIVE_PROCESSORS], ecx
845 lock xor [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
849 test dword ptr [edx+KPROCESS_ACTIVE_PROCESSORS], 0
851 test dword ptr [eax+KPROCESS_ACTIVE_PROCESSORS], 0
856 /* Check if their LDTs changed */
857 mov ecx, [edx+KPROCESS_LDT_DESCRIPTOR0]
858 or ecx, [eax+KPROCESS_LDT_DESCRIPTOR0]
862 mov eax, [edx+KPROCESS_DIRECTORY_TABLE_BASE]
866 mov ecx, fs:[KPCR_TSS]
868 /* Clear GS on process swap */
872 /* Update IOPM offset */
873 mov ax, [edx+KPROCESS_IOPM_OFFSET]
874 mov [ecx+KTSS_IOMAPBASE], ax