2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/i386/thread.c
5 * PURPOSE: i386 Thread Context Creation
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES ******************************************************************/
15 typedef struct _KSWITCHFRAME
18 BOOLEAN ApcBypassDisable
;
20 } KSWITCHFRAME
, *PKSWITCHFRAME
;
22 typedef struct _KSTART_FRAME
24 PKSYSTEM_ROUTINE SystemRoutine
;
25 PKSTART_ROUTINE StartRoutine
;
28 } KSTART_FRAME
, *PKSTART_FRAME
;
30 typedef struct _KUINIT_FRAME
32 KSWITCHFRAME CtxSwitchFrame
;
33 KSTART_FRAME StartFrame
;
34 KTRAP_FRAME TrapFrame
;
35 FX_SAVE_AREA FxSaveArea
;
36 } KUINIT_FRAME
, *PKUINIT_FRAME
;
38 typedef struct _KKINIT_FRAME
40 KSWITCHFRAME CtxSwitchFrame
;
41 KSTART_FRAME StartFrame
;
42 FX_SAVE_AREA FxSaveArea
;
43 } KKINIT_FRAME
, *PKKINIT_FRAME
;
48 IN PKTHREAD OldThread
,
54 KiRetireDpcListInDpcStack(
59 /* FUNCTIONS *****************************************************************/
65 PKTRAP_FRAME TrapFrame
;
66 PKSTART_FRAME StartFrame
;
67 PKUINIT_FRAME InitFrame
;
69 /* Get the start and trap frames */
70 InitFrame
= KeGetCurrentThread()->KernelStack
;
71 StartFrame
= &InitFrame
->StartFrame
;
72 TrapFrame
= &InitFrame
->TrapFrame
;
74 /* Lower to APC level */
75 KfLowerIrql(APC_LEVEL
);
77 /* Call the system routine */
78 StartFrame
->SystemRoutine(StartFrame
->StartRoutine
, StartFrame
->StartContext
);
80 /* If we returned, we better be a user thread */
81 if (!StartFrame
->UserThread
) DbgBreakPoint();
83 /* Exit to user-mode */
84 KiServiceExit2(TrapFrame
);
89 KiInitializeContextThread(IN PKTHREAD Thread
,
90 IN PKSYSTEM_ROUTINE SystemRoutine
,
91 IN PKSTART_ROUTINE StartRoutine
,
92 IN PVOID StartContext
,
93 IN PCONTEXT ContextPointer
)
95 PFX_SAVE_AREA FxSaveArea
;
96 PFXSAVE_FORMAT FxSaveFormat
;
97 PKSTART_FRAME StartFrame
;
98 PKSWITCHFRAME CtxSwitchFrame
;
99 PKTRAP_FRAME TrapFrame
;
100 CONTEXT LocalContext
;
101 PCONTEXT Context
= NULL
;
104 /* Check if this is a With-Context Thread */
107 /* Set up the Initial Frame */
108 PKUINIT_FRAME InitFrame
;
109 InitFrame
= (PKUINIT_FRAME
)((ULONG_PTR
)Thread
->InitialStack
-
110 sizeof(KUINIT_FRAME
));
112 /* Copy over the context we got */
113 RtlCopyMemory(&LocalContext
, ContextPointer
, sizeof(CONTEXT
));
114 Context
= &LocalContext
;
115 ContextFlags
= CONTEXT_CONTROL
;
117 /* Zero out the trap frame and save area */
118 RtlZeroMemory(&InitFrame
->TrapFrame
,
119 KTRAP_FRAME_LENGTH
+ sizeof(FX_SAVE_AREA
));
121 /* Setup the Fx Area */
122 FxSaveArea
= &InitFrame
->FxSaveArea
;
124 /* Check if we support FXsr */
125 if (KeI386FxsrPresent
)
127 /* Get the FX Save Format Area */
128 FxSaveFormat
= (PFXSAVE_FORMAT
)Context
->ExtendedRegisters
;
130 /* Set an initial state */
131 FxSaveFormat
->ControlWord
= 0x27F;
132 FxSaveFormat
->StatusWord
= 0;
133 FxSaveFormat
->TagWord
= 0;
134 FxSaveFormat
->ErrorOffset
= 0;
135 FxSaveFormat
->ErrorSelector
= 0;
136 FxSaveFormat
->DataOffset
= 0;
137 FxSaveFormat
->DataSelector
= 0;
138 FxSaveFormat
->MXCsr
= 0x1F80;
142 /* Setup the regular save area */
143 Context
->FloatSave
.ControlWord
= 0x27F;
144 Context
->FloatSave
.StatusWord
= 0;
145 Context
->FloatSave
.TagWord
= -1;
146 Context
->FloatSave
.ErrorOffset
= 0;
147 Context
->FloatSave
.ErrorSelector
= 0;
148 Context
->FloatSave
.DataOffset
=0;
149 Context
->FloatSave
.DataSelector
= 0;
152 /* Check if the CPU has NPX */
153 if (KeI386NpxPresent
)
155 /* Set an intial NPX State */
156 Context
->FloatSave
.Cr0NpxState
= 0;
157 FxSaveArea
->Cr0NpxState
= 0;
158 FxSaveArea
->NpxSavedCpu
= 0;
160 /* Now set the context flags depending on XMM support */
161 ContextFlags
|= (KeI386FxsrPresent
) ? CONTEXT_EXTENDED_REGISTERS
:
162 CONTEXT_FLOATING_POINT
;
164 /* Set the Thread's NPX State */
165 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
166 Thread
->DispatcherHeader
.NpxIrql
= PASSIVE_LEVEL
;
170 /* We'll use emulation */
171 FxSaveArea
->Cr0NpxState
= CR0_EM
;
172 Thread
->NpxState
= NPX_STATE_NOT_LOADED
&~ CR0_MP
;
175 /* Disable any debug registers */
176 Context
->ContextFlags
&= ~CONTEXT_DEBUG_REGISTERS
;
178 /* Setup the Trap Frame */
179 TrapFrame
= &InitFrame
->TrapFrame
;
181 /* Set up a trap frame from the context. */
182 KeContextToTrapFrame(Context
,
185 Context
->ContextFlags
| ContextFlags
,
188 /* Set SS, DS, ES's RPL Mask properly */
189 TrapFrame
->HardwareSegSs
|= RPL_MASK
;
190 TrapFrame
->SegDs
|= RPL_MASK
;
191 TrapFrame
->SegEs
|= RPL_MASK
;
194 /* Set the debug mark */
195 TrapFrame
->DbgArgMark
= 0xBADB0D00;
197 /* Set the previous mode as user */
198 TrapFrame
->PreviousPreviousMode
= UserMode
;
200 /* Terminate the Exception Handler List */
201 TrapFrame
->ExceptionList
= EXCEPTION_CHAIN_END
;
203 /* Setup the Stack for KiThreadStartup and Context Switching */
204 StartFrame
= &InitFrame
->StartFrame
;
205 CtxSwitchFrame
= &InitFrame
->CtxSwitchFrame
;
207 /* Tell the thread it will run in User Mode */
208 Thread
->PreviousMode
= UserMode
;
210 /* Tell KiThreadStartup of that too */
211 StartFrame
->UserThread
= TRUE
;
215 /* Set up the Initial Frame for the system thread */
216 PKKINIT_FRAME InitFrame
;
217 InitFrame
= (PKKINIT_FRAME
)((ULONG_PTR
)Thread
->InitialStack
-
218 sizeof(KKINIT_FRAME
));
220 /* Setup the Fx Area */
221 FxSaveArea
= &InitFrame
->FxSaveArea
;
222 RtlZeroMemory(FxSaveArea
, sizeof(FX_SAVE_AREA
));
224 /* Check if we have Fxsr support */
225 if (KeI386FxsrPresent
)
227 /* Set the stub FX area */
228 FxSaveArea
->U
.FxArea
.ControlWord
= 0x27F;
229 FxSaveArea
->U
.FxArea
.MXCsr
= 0x1F80;
233 /* Set the stub FN area */
234 FxSaveArea
->U
.FnArea
.ControlWord
= 0x27F;
235 FxSaveArea
->U
.FnArea
.TagWord
= -1;
239 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
241 /* Setup the Stack for KiThreadStartup and Context Switching */
242 StartFrame
= &InitFrame
->StartFrame
;
243 CtxSwitchFrame
= &InitFrame
->CtxSwitchFrame
;
245 /* Tell the thread it will run in Kernel Mode */
246 Thread
->PreviousMode
= KernelMode
;
248 /* Tell KiThreadStartup of that too */
249 StartFrame
->UserThread
= FALSE
;
252 /* Now setup the remaining data for KiThreadStartup */
253 StartFrame
->StartContext
= StartContext
;
254 StartFrame
->StartRoutine
= StartRoutine
;
255 StartFrame
->SystemRoutine
= SystemRoutine
;
257 /* And set up the Context Switch Frame */
258 CtxSwitchFrame
->RetAddr
= KiThreadStartup
;
259 CtxSwitchFrame
->ApcBypassDisable
= TRUE
;
260 CtxSwitchFrame
->ExceptionList
= EXCEPTION_CHAIN_END
;
262 /* Save back the new value of the kernel stack. */
263 Thread
->KernelStack
= (PVOID
)CtxSwitchFrame
;
270 PKPRCB Prcb
= KeGetCurrentPrcb();
271 PKTHREAD OldThread
, NewThread
;
273 /* Initialize the idle loop: disable interrupts */
279 /* Now loop forever */
282 /* Check for pending timers, pending DPCs, or pending ready threads */
283 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
284 (Prcb
->TimerRequest
) ||
285 (Prcb
->DeferredReadyListHead
.Next
))
287 /* Quiesce the DPC software interrupt */
288 HalClearSoftwareInterrupt(DISPATCH_LEVEL
);
291 KiRetireDpcList(Prcb
);
294 /* Check if a new thread is scheduled for execution */
295 if (Prcb
->NextThread
)
297 /* Enable interupts */
300 /* Capture current thread data */
301 OldThread
= Prcb
->CurrentThread
;
302 NewThread
= Prcb
->NextThread
;
304 /* Set new thread data */
305 Prcb
->NextThread
= NULL
;
306 Prcb
->CurrentThread
= NewThread
;
308 /* The thread is now running */
309 NewThread
->State
= Running
;
311 /* Switch away from the idle thread */
312 KiSwapContext(APC_LEVEL
, OldThread
);
314 /* We are back in the idle thread -- disable interrupts again */
322 /* Continue staying idle. Note the HAL returns with interrupts on */
323 Prcb
->PowerState
.IdleFunction(&Prcb
->PowerState
);
330 KiSwapContextExit(IN PKTHREAD OldThread
,
331 IN PKSWITCHFRAME SwitchFrame
)
333 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
334 PKPROCESS OldProcess
, NewProcess
;
337 PKUINIT_FRAME InitFrame
;
339 /* We are on the new thread stack now */
340 NewThread
= Pcr
->PrcbData
.CurrentThread
;
342 /* Now we are the new thread. Check if it's in a new process */
343 OldProcess
= OldThread
->ApcState
.Process
;
344 NewProcess
= NewThread
->ApcState
.Process
;
345 if (OldProcess
!= NewProcess
)
347 /* Check if there is a different LDT */
348 if (*(PULONGLONG
)&OldProcess
->LdtDescriptor
!= *(PULONGLONG
)&NewProcess
->LdtDescriptor
)
350 DPRINT1("LDT switch not implemented\n");
354 /* Switch address space and flush TLB */
355 __writecr3(NewProcess
->DirectoryTableBase
[0]);
362 Pcr
->NtTib
.Self
= (PVOID
)NewThread
->Teb
;
363 GdtEntry
= &Pcr
->GDT
[KGDT_R3_TEB
/ sizeof(KGDTENTRY
)];
364 GdtEntry
->BaseLow
= (USHORT
)((ULONG_PTR
)NewThread
->Teb
& 0xFFFF);
365 GdtEntry
->HighWord
.Bytes
.BaseMid
= (UCHAR
)((ULONG_PTR
)NewThread
->Teb
>> 16);
366 GdtEntry
->HighWord
.Bytes
.BaseHi
= (UCHAR
)((ULONG_PTR
)NewThread
->Teb
>> 24);
368 /* Set new TSS fields */
369 InitFrame
= (PKUINIT_FRAME
)NewThread
->InitialStack
- 1;
370 Pcr
->TSS
->Esp0
= (ULONG_PTR
)&InitFrame
->TrapFrame
;
371 if (!(InitFrame
->TrapFrame
.EFlags
& EFLAGS_V86_MASK
))
373 Pcr
->TSS
->Esp0
-= (FIELD_OFFSET(KTRAP_FRAME
, V86Gs
) - FIELD_OFFSET(KTRAP_FRAME
, HardwareSegSs
));
375 Pcr
->TSS
->IoMapBase
= NewProcess
->IopmOffset
;
377 /* Increase thread context switches */
378 NewThread
->ContextSwitches
++;
380 /* Load data from switch frame */
381 Pcr
->NtTib
.ExceptionList
= SwitchFrame
->ExceptionList
;
383 /* DPCs shouldn't be active */
384 if (Pcr
->PrcbData
.DpcRoutineActive
)
386 /* Crash the machine */
387 KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC
,
388 (ULONG_PTR
)OldThread
,
389 (ULONG_PTR
)NewThread
,
390 (ULONG_PTR
)OldThread
->InitialStack
,
394 /* Kernel APCs may be pending */
395 if (NewThread
->ApcState
.KernelApcPending
)
397 /* Are APCs enabled? */
398 if (!NewThread
->SpecialApcDisable
)
400 /* Request APC delivery */
401 if (SwitchFrame
->ApcBypassDisable
) HalRequestSoftwareInterrupt(APC_LEVEL
);
412 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame
,
413 IN ULONG_PTR OldThreadAndApcFlag
)
415 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
416 PKTHREAD OldThread
, NewThread
;
419 /* Save APC bypass disable */
420 SwitchFrame
->ApcBypassDisable
= OldThreadAndApcFlag
& 3;
421 SwitchFrame
->ExceptionList
= Pcr
->NtTib
.ExceptionList
;
423 /* Increase context switch count and check if tracing is enabled */
424 Pcr
->ContextSwitches
++;
425 if (Pcr
->PerfGlobalGroupMask
)
427 /* We don't support this yet on x86 either */
428 DPRINT1("WMI Tracing not supported\n");
432 /* Get thread pointers */
433 OldThread
= (PKTHREAD
)(OldThreadAndApcFlag
& ~3);
434 NewThread
= Pcr
->PrcbData
.CurrentThread
;
436 /* Get the old thread and set its kernel stack */
437 OldThread
->KernelStack
= SwitchFrame
;
439 /* ISRs can change FPU state, so disable interrupts while checking */
442 /* Get current and new CR0 and check if they've changed */
444 NewCr0
= NewThread
->NpxState
|
445 (Cr0
& ~(CR0_MP
| CR0_EM
| CR0_TS
)) |
446 ((PKUINIT_FRAME
)NewThread
->InitialStack
- 1)->FxSaveArea
.Cr0NpxState
;
447 if (Cr0
!= NewCr0
) __writecr0(NewCr0
);
449 /* Now enable interrupts and do the switch */
451 KiSwitchThreads(OldThread
, NewThread
->KernelStack
);
456 KiDispatchInterrupt(VOID
)
458 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
459 PKPRCB Prcb
= &Pcr
->PrcbData
;
461 PKTHREAD NewThread
, OldThread
;
463 /* Disable interrupts */
466 /* Check for pending timers, pending DPCs, or pending ready threads */
467 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
468 (Prcb
->TimerRequest
) ||
469 (Prcb
->DeferredReadyListHead
.Next
))
471 /* Switch to safe execution context */
472 OldHandler
= Pcr
->NtTib
.ExceptionList
;
473 Pcr
->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
475 /* Retire DPCs while under the DPC stack */
476 KiRetireDpcListInDpcStack(Prcb
, Prcb
->DpcStack
);
478 /* Restore context */
479 Pcr
->NtTib
.ExceptionList
= OldHandler
;
482 /* Re-enable interrupts */
485 /* Check for quantum end */
486 if (Prcb
->QuantumEnd
)
488 /* Handle quantum end */
489 Prcb
->QuantumEnd
= FALSE
;
492 else if (Prcb
->NextThread
)
494 /* Capture current thread data */
495 OldThread
= Prcb
->CurrentThread
;
496 NewThread
= Prcb
->NextThread
;
498 /* Set new thread data */
499 Prcb
->NextThread
= NULL
;
500 Prcb
->CurrentThread
= NewThread
;
502 /* The thread is now running */
503 NewThread
->State
= Running
;
504 OldThread
->WaitReason
= WrDispatchInt
;
506 /* Make the old thread ready */
507 KxQueueReadyThread(OldThread
, Prcb
);
509 /* Swap to the new thread */
510 KiSwapContext(APC_LEVEL
, OldThread
);