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 /* FUNCTIONS ****************************************************************/
23 * KiSwapContextInternal
25 * The KiSwapContextInternal routine switches context to another thread.
28 * ESI - Pointer to the KTHREAD to which the caller wishes to
30 * EDI - Pointer to the KTHREAD to which the caller wishes to
37 * Absolutely all registers except ESP can be trampled here for maximum code flexibility.
40 .globl @KiSwapContextInternal@0
41 .func @KiSwapContextInternal@0, @KiSwapContextInternal@0
42 @KiSwapContextInternal@0:
49 /* Acquire the swap lock */
50 cmp byte ptr [esi+KTHREAD_SWAP_BUSY], 0
56 /* Increase context switches (use ES for lazy load) */
57 inc dword ptr es:[ebx+KPCR_CONTEXT_SWITCHES]
59 /* Save the Exception list */
60 push [ebx+KPCR_EXCEPTION_LIST]
63 cmp dword ptr [ebx+KPCR_PERF_GLOBAL_GROUP_MASK], 0
69 /* Assert that we're on the right CPU */
70 mov cl, [esi+KTHREAD_NEXT_PROCESSOR]
71 cmp cl, [ebx+KPCR_PROCESSOR_NUMBER]
76 /* Get CR0 and save it */
82 cmp byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_LOADED
88 mov [edi+KTHREAD_KERNEL_STACK], esp
90 /* Checking NPX, disable interrupts now */
91 mov eax, [esi+KTHREAD_INITIAL_STACK]
94 /* Get the NPX State */
95 movzx ecx, byte ptr [esi+KTHREAD_NPX_STATE]
97 /* Clear the other bits, merge in CR0, merge in FPU CR0 bits and compare */
98 and edx, ~(CR0_MP + CR0_EM + CR0_TS)
100 or ecx, [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)]
105 /* Enable interrupts and set the current stack */
107 mov esp, [esi+KTHREAD_KERNEL_STACK]
109 /* Check if address space switch is needed */
110 mov ebp, [esi+KTHREAD_APCSTATE_PROCESS]
111 mov eax, [edi+KTHREAD_APCSTATE_PROCESS]
116 /* Get the active processors and XOR with the process' */
117 mov ecx, [ebx+KPCR_SET_MEMBER_COPY]
118 lock xor [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
119 lock xor [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
121 /* Assert change went ok */
123 test [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
125 test [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
130 /* Check if we need an LDT */
131 mov ecx, [ebp+KPROCESS_LDT_DESCRIPTOR0]
132 or ecx, [eax+KPROCESS_LDT_DESCRIPTOR0]
136 /* Switch address space */
137 mov eax, [ebp+KPROCESS_DIRECTORY_TABLE_BASE]
143 /* Release swap lock */
144 and byte ptr [edi+KTHREAD_SWAP_BUSY], 0
152 mov eax, [esi+KTHREAD_TEB]
153 mov [ebx+KPCR_TEB], eax
154 mov ecx, [ebx+KPCR_GDT]
160 /* Get stack pointer */
161 mov eax, [esi+KTHREAD_INITIAL_STACK]
163 /* Make space for the NPX Frame */
164 sub eax, NPX_FRAME_LENGTH
166 /* Check if this isn't V86 Mode, so we can bias the Esp0 */
167 test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
171 sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
176 mov ecx, [ebx+KPCR_TSS]
177 mov [ecx+KTSS_ESP0], eax
179 /* Set current IOPM offset in the TSS */
180 mov ax, [ebp+KPROCESS_IOPM_OFFSET]
181 mov [ecx+KTSS_IOMAPBASE], ax
183 /* Increase context switches */
184 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
186 /* Restore exception list */
187 pop [ebx+KPCR_EXCEPTION_LIST]
192 /* DPC shouldn't be active */
193 cmp byte ptr [ebx+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
196 /* Check if kernel APCs are pending */
197 cmp byte ptr [esi+KTHREAD_PENDING_KERNEL_APC], 0
200 /* No APCs, return */
206 /* Check if they're disabled */
207 cmp word ptr [esi+KTHREAD_SPECIAL_APC_DISABLE], 0
212 /* Request APC Delivery */
214 call @HalRequestSoftwareInterrupt@4
219 /* Return with APC pending */
224 /* Check if it's empty */
225 mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR0]
229 /* Write the LDT Selector */
230 mov ecx, [ebx+KPCR_GDT]
231 mov [ecx+KGDT_LDT], eax
232 mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR1]
233 mov [ecx+KGDT_LDT+4], eax
235 /* Write the INT21 handler */
236 mov ecx, [ebx+KPCR_IDT]
237 mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR0]
239 mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR1]
242 /* Save LDT Selector */
252 /* Assert NPX State */
253 test byte ptr [esi+KTHREAD_NPX_STATE], ~(NPX_STATE_NOT_LOADED)
255 test dword ptr [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)], ~(CR0_PE + CR0_MP + CR0_EM + CR0_TS)
266 /* Mask out FPU flags */
267 and edx, ~(CR0_MP + CR0_EM + CR0_TS)
269 /* Get the NPX Frame */
270 mov ecx, [edi+KTHREAD_INITIAL_STACK]
271 sub ecx, NPX_FRAME_LENGTH
273 /* Check if we have a new CR0 */
277 /* We do, update it */
283 /* Save the NPX State */
285 mov byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
287 /* Clear the NPX Thread */
288 mov dword ptr [ebx+KPCR_NPX_THREAD], 0
296 /* No WMI support yet */
304 /* Bugcheck the machine, printing out the threads being switched */
305 mov eax, [edi+KTHREAD_INITIAL_STACK]
310 push ATTEMPTED_SWITCH_FROM_DPC
311 call _KeBugCheckEx@20
326 * The KiSwapContext routine switches context to another thread.
329 * TargetThread - Pointer to the KTHREAD to which the caller wishes to
333 * The WaitStatus of the Target Thread.
336 * This is a wrapper around KiSwapContextInternal which will save all the
337 * non-volatile registers so that the Internal function can use all of
338 * them. It will also save the old current thread and set the new one.
340 * The calling thread does not return after KiSwapContextInternal until
341 * another thread switches to IT.
344 .globl @KiSwapContext@8
345 .func @KiSwapContext@8, @KiSwapContext@8
348 /* Save 4 registers */
351 /* Save all the non-volatile ones */
357 /* Get the current KPCR */
358 mov ebx, fs:[KPCR_SELF]
360 /* Get the Current Thread */
363 /* Get the New Thread */
366 /* Get the wait IRQL */
367 movzx ecx, byte ptr [edi+KTHREAD_WAIT_IRQL]
369 /* Do the swap with the registers correctly setup */
370 call @KiSwapContextInternal@0
372 /* Return the registers */
383 /* DPC INTERRUPT HANDLER ******************************************************/
385 .globl _KiDispatchInterrupt@0
386 .func KiDispatchInterrupt@0
387 _KiDispatchInterrupt@0:
392 /* Get the PCR and disable interrupts */
393 mov ebx, PCR[KPCR_SELF]
396 /* Check if we have to deliver DPCs, timers, or deferred threads */
397 mov eax, [ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]
398 or eax, [ebx+KPCR_PRCB_TIMER_REQUEST]
399 or eax, [ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]
402 /* Save stack pointer and exception list, then clear it */
404 push dword ptr [ebx+KPCR_EXCEPTION_LIST]
405 mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
407 /* Save the stack and switch to the DPC Stack */
409 mov esp, [ebx+KPCR_PRCB_DPC_STACK]
413 mov ecx, [ebx+KPCR_PRCB]
414 call @KiRetireDpcList@4
416 /* Restore stack and exception list */
418 pop dword ptr [ebx+KPCR_EXCEPTION_LIST]
423 /* Re-enable interrupts */
426 /* Check if we have quantum end */
427 cmp byte ptr [ebx+KPCR_PRCB_QUANTUM_END], 0
430 /* Check if we have a thread to swap to */
431 cmp byte ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
434 /* Make space on the stack to save registers */
440 /* Get the current thread */
441 mov edi, [ebx+KPCR_CURRENT_THREAD]
444 /* Raise to synch level */
445 call _KeRaiseIrqlToSynchLevel@0
447 /* Set context swap busy */
448 mov byte ptr [edi+KTHREAD_SWAP_BUSY], 1
450 /* Acquire the PRCB Lock */
451 lock bts dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
453 lea ecx, [ebx+KPCR_PRCB_PRCB_LOCK]
454 call @KefAcquireSpinLockAtDpcLevel@4
458 /* Get the next thread and clear it */
459 mov esi, [ebx+KPCR_PRCB_NEXT_THREAD]
460 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
462 /* Set us as the current running thread */
463 mov [ebx+KPCR_CURRENT_THREAD], esi
464 mov byte ptr [esi+KTHREAD_STATE_], Running
465 mov byte ptr [edi+KTHREAD_WAIT_REASON], WrDispatchInt
467 /* Put thread in ECX and get the PRCB in EDX */
469 lea edx, [ebx+KPCR_PRCB_DATA]
470 call @KiQueueReadyThread@8
472 /* Set APC_LEVEL and do the swap */
474 call @KiSwapContextInternal@0
477 /* Lower IRQL back to dispatch */
478 mov cl, DISPATCH_LEVEL
482 /* Restore registers */
494 /* Disable quantum end and process it */
495 mov byte ptr [ebx+KPCR_PRCB_QUANTUM_END], 0
502 .func @KiIdleLoop@0, @KiIdleLoop@0
506 mov ebx, fs:[KPCR_SELF]
508 /* Jump into mainline code */
512 /* Call the CPU's idle function */
513 lea ecx, [ebx+KPCR_PRCB_POWER_STATE_IDLE_FUNCTION]
517 /* Cycle interrupts for 1 cycle */
523 /* Check if we have to deliver DPCs, timers, or deferred threads */
524 mov eax, [ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]
525 or eax, [ebx+KPCR_PRCB_TIMER_REQUEST]
527 or eax, [ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]
531 mov cl, DISPATCH_LEVEL
532 call @HalClearSoftwareInterrupt@4
534 /* Handle the above */
535 lea ecx, [ebx+KPCR_PRCB_DATA]
536 call @KiRetireDpcList@4
539 /* Check if a next thread is queued */
540 cmp dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
548 /* There is, raise IRQL to synch level */
549 call _KeRaiseIrqlToSynchLevel@0
553 /* Set the current thread to ready */
554 mov edi, [ebx+KPCR_CURRENT_THREAD]
556 mov byte ptr [edi+KTHREAD_SWAP_BUSY], 1
558 /* Acquire the PRCB Lock */
559 lock bts dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
561 lea ecx, [ebx+KPCR_PRCB_PRCB_LOCK]
562 call @KefAcquireSpinLockAtDpcLevel@4
566 /* Check if the next thread is the current */
567 mov esi, [ebx+KPCR_PRCB_NEXT_THREAD]
573 /* Clear the next thread and set this one instead */
574 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
575 mov [ebx+KPCR_CURRENT_THREAD], esi
577 /* Set the thread as running */
578 mov byte ptr [esi+KTHREAD_STATE_], Running
581 /* Disable the idle scheduler and release the PRCB lock */
582 and byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
583 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
587 /* ReactOS Mm Hack */
589 call @MiSyncForContextSwitch@4
591 /* Swap context at APC_LEVEL */
593 call @KiSwapContextInternal@0
596 /* Lower to DPC level */
597 mov ecx, DISPATCH_LEVEL
604 /* Clear the next thread, and put the thread as ready after lock release */
605 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
606 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
607 and byte ptr [edi+KTHREAD_STATE_], Ready
611 /* Check if the idle scheduler is enabled */
612 cmp byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
615 /* It is, so call the scheduler */
616 lea ecx, [ebx+KPCR_PRCB_DATA]
617 call @KiIdleSchedule@4
620 /* Get new thread pointers and either swap or idle loop again */
622 mov edi, [ebx+KPCR_PRCB_IDLE_THREAD]
628 /* FIXFIX: Move to C code ****/
629 .globl _Ki386SetupAndExitToV86Mode@4
630 .func Ki386SetupAndExitToV86Mode@4
631 _Ki386SetupAndExitToV86Mode@4:
633 /* Enter V8086 mode */
635 sub esp, (12 + KTRAP_FRAME_LENGTH + NPX_FRAME_LENGTH)
637 call @KiEnterV86Mode@4
641 .globl @Ki386BiosCallReturnAddress@4
642 @Ki386BiosCallReturnAddress@4:
644 /* Exit V8086 mode */
645 call @KiExitV86Mode@4
647 add esp, (12 + KTRAP_FRAME_LENGTH + NPX_FRAME_LENGTH)