2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/i386/thrdini.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
)
83 KeBugCheck(NO_USER_MODE_CONTEXT
);
86 /* Exit to user-mode */
87 KiServiceExit2(TrapFrame
);
92 KiInitializeContextThread(IN PKTHREAD Thread
,
93 IN PKSYSTEM_ROUTINE SystemRoutine
,
94 IN PKSTART_ROUTINE StartRoutine
,
95 IN PVOID StartContext
,
96 IN PCONTEXT ContextPointer
)
98 PFX_SAVE_AREA FxSaveArea
;
99 PFXSAVE_FORMAT FxSaveFormat
;
100 PKSTART_FRAME StartFrame
;
101 PKSWITCHFRAME CtxSwitchFrame
;
102 PKTRAP_FRAME TrapFrame
;
103 CONTEXT LocalContext
;
104 PCONTEXT Context
= NULL
;
107 /* Check if this is a With-Context Thread */
110 /* Set up the Initial Frame */
111 PKUINIT_FRAME InitFrame
;
112 InitFrame
= (PKUINIT_FRAME
)((ULONG_PTR
)Thread
->InitialStack
-
113 sizeof(KUINIT_FRAME
));
115 /* Copy over the context we got */
116 RtlCopyMemory(&LocalContext
, ContextPointer
, sizeof(CONTEXT
));
117 Context
= &LocalContext
;
118 ContextFlags
= CONTEXT_CONTROL
;
120 /* Zero out the trap frame and save area */
121 RtlZeroMemory(&InitFrame
->TrapFrame
,
122 KTRAP_FRAME_LENGTH
+ sizeof(FX_SAVE_AREA
));
124 /* Setup the Fx Area */
125 FxSaveArea
= &InitFrame
->FxSaveArea
;
127 /* Check if we support FXsr */
128 if (KeI386FxsrPresent
)
130 /* Get the FX Save Format Area */
131 FxSaveFormat
= (PFXSAVE_FORMAT
)Context
->ExtendedRegisters
;
133 /* Set an initial state */
134 FxSaveFormat
->ControlWord
= 0x27F;
135 FxSaveFormat
->StatusWord
= 0;
136 FxSaveFormat
->TagWord
= 0;
137 FxSaveFormat
->ErrorOffset
= 0;
138 FxSaveFormat
->ErrorSelector
= 0;
139 FxSaveFormat
->DataOffset
= 0;
140 FxSaveFormat
->DataSelector
= 0;
141 FxSaveFormat
->MXCsr
= 0x1F80;
145 /* Setup the regular save area */
146 Context
->FloatSave
.ControlWord
= 0x27F;
147 Context
->FloatSave
.StatusWord
= 0;
148 Context
->FloatSave
.TagWord
= -1;
149 Context
->FloatSave
.ErrorOffset
= 0;
150 Context
->FloatSave
.ErrorSelector
= 0;
151 Context
->FloatSave
.DataOffset
=0;
152 Context
->FloatSave
.DataSelector
= 0;
155 /* Check if the CPU has NPX */
156 if (KeI386NpxPresent
)
158 /* Set an intial NPX State */
159 Context
->FloatSave
.Cr0NpxState
= 0;
160 FxSaveArea
->Cr0NpxState
= 0;
161 FxSaveArea
->NpxSavedCpu
= 0;
163 /* Now set the context flags depending on XMM support */
164 ContextFlags
|= (KeI386FxsrPresent
) ? CONTEXT_EXTENDED_REGISTERS
:
165 CONTEXT_FLOATING_POINT
;
167 /* Set the Thread's NPX State */
168 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
169 Thread
->Header
.NpxIrql
= PASSIVE_LEVEL
;
173 /* We'll use emulation */
174 FxSaveArea
->Cr0NpxState
= CR0_EM
;
175 Thread
->NpxState
= NPX_STATE_NOT_LOADED
&~ CR0_MP
;
178 /* Disable any debug registers */
179 Context
->ContextFlags
&= ~CONTEXT_DEBUG_REGISTERS
;
181 /* Setup the Trap Frame */
182 TrapFrame
= &InitFrame
->TrapFrame
;
184 /* Set up a trap frame from the context. */
185 KeContextToTrapFrame(Context
,
188 Context
->ContextFlags
| ContextFlags
,
191 /* Set SS, DS, ES's RPL Mask properly */
192 TrapFrame
->HardwareSegSs
|= RPL_MASK
;
193 TrapFrame
->SegDs
|= RPL_MASK
;
194 TrapFrame
->SegEs
|= RPL_MASK
;
197 /* Set the debug mark */
198 TrapFrame
->DbgArgMark
= 0xBADB0D00;
200 /* Set the previous mode as user */
201 TrapFrame
->PreviousPreviousMode
= UserMode
;
203 /* Terminate the Exception Handler List */
204 TrapFrame
->ExceptionList
= EXCEPTION_CHAIN_END
;
206 /* Setup the Stack for KiThreadStartup and Context Switching */
207 StartFrame
= &InitFrame
->StartFrame
;
208 CtxSwitchFrame
= &InitFrame
->CtxSwitchFrame
;
210 /* Tell the thread it will run in User Mode */
211 Thread
->PreviousMode
= UserMode
;
213 /* Tell KiThreadStartup of that too */
214 StartFrame
->UserThread
= TRUE
;
218 /* Set up the Initial Frame for the system thread */
219 PKKINIT_FRAME InitFrame
;
220 InitFrame
= (PKKINIT_FRAME
)((ULONG_PTR
)Thread
->InitialStack
-
221 sizeof(KKINIT_FRAME
));
223 /* Setup the Fx Area */
224 FxSaveArea
= &InitFrame
->FxSaveArea
;
225 RtlZeroMemory(FxSaveArea
, sizeof(FX_SAVE_AREA
));
227 /* Check if we have Fxsr support */
228 if (KeI386FxsrPresent
)
230 /* Set the stub FX area */
231 FxSaveArea
->U
.FxArea
.ControlWord
= 0x27F;
232 FxSaveArea
->U
.FxArea
.MXCsr
= 0x1F80;
236 /* Set the stub FN area */
237 FxSaveArea
->U
.FnArea
.ControlWord
= 0x27F;
238 FxSaveArea
->U
.FnArea
.TagWord
= -1;
242 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
244 /* Setup the Stack for KiThreadStartup and Context Switching */
245 StartFrame
= &InitFrame
->StartFrame
;
246 CtxSwitchFrame
= &InitFrame
->CtxSwitchFrame
;
248 /* Tell the thread it will run in Kernel Mode */
249 Thread
->PreviousMode
= KernelMode
;
251 /* Tell KiThreadStartup of that too */
252 StartFrame
->UserThread
= FALSE
;
255 /* Now setup the remaining data for KiThreadStartup */
256 StartFrame
->StartContext
= StartContext
;
257 StartFrame
->StartRoutine
= StartRoutine
;
258 StartFrame
->SystemRoutine
= SystemRoutine
;
260 /* And set up the Context Switch Frame */
261 CtxSwitchFrame
->RetAddr
= KiThreadStartup
;
262 CtxSwitchFrame
->ApcBypassDisable
= TRUE
;
263 CtxSwitchFrame
->ExceptionList
= EXCEPTION_CHAIN_END
;
265 /* Save back the new value of the kernel stack. */
266 Thread
->KernelStack
= (PVOID
)CtxSwitchFrame
;
273 PKPRCB Prcb
= KeGetCurrentPrcb();
274 PKTHREAD OldThread
, NewThread
;
276 /* Now loop forever */
279 /* Start of the idle loop: disable interrupts */
285 /* Check for pending timers, pending DPCs, or pending ready threads */
286 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
287 (Prcb
->TimerRequest
) ||
288 (Prcb
->DeferredReadyListHead
.Next
))
290 /* Quiesce the DPC software interrupt */
291 HalClearSoftwareInterrupt(DISPATCH_LEVEL
);
294 KiRetireDpcList(Prcb
);
297 /* Check if a new thread is scheduled for execution */
298 if (Prcb
->NextThread
)
300 /* Enable interrupts */
303 /* Capture current thread data */
304 OldThread
= Prcb
->CurrentThread
;
305 NewThread
= Prcb
->NextThread
;
307 /* Set new thread data */
308 Prcb
->NextThread
= NULL
;
309 Prcb
->CurrentThread
= NewThread
;
311 /* The thread is now running */
312 NewThread
->State
= Running
;
314 /* Switch away from the idle thread */
315 KiSwapContext(APC_LEVEL
, OldThread
);
319 /* Continue staying idle. Note the HAL returns with interrupts on */
320 Prcb
->PowerState
.IdleFunction(&Prcb
->PowerState
);
327 KiSwapContextExit(IN PKTHREAD OldThread
,
328 IN PKSWITCHFRAME SwitchFrame
)
330 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
331 PKPROCESS OldProcess
, NewProcess
;
334 /* We are on the new thread stack now */
335 NewThread
= Pcr
->PrcbData
.CurrentThread
;
337 /* Now we are the new thread. Check if it's in a new process */
338 OldProcess
= OldThread
->ApcState
.Process
;
339 NewProcess
= NewThread
->ApcState
.Process
;
340 if (OldProcess
!= NewProcess
)
342 /* Check if there is a different LDT */
343 if (*(PULONGLONG
)&OldProcess
->LdtDescriptor
!= *(PULONGLONG
)&NewProcess
->LdtDescriptor
)
345 if (NewProcess
->LdtDescriptor
.LimitLow
)
347 KeSetGdtSelector(KGDT_LDT
,
348 ((PULONG
)&NewProcess
->LdtDescriptor
)[0],
349 ((PULONG
)&NewProcess
->LdtDescriptor
)[1]);
350 Ke386SetLocalDescriptorTable(KGDT_LDT
);
354 Ke386SetLocalDescriptorTable(0);
358 /* Switch address space and flush TLB */
359 __writecr3(NewProcess
->DirectoryTableBase
[0]);
366 KiSetTebBase((PKPCR
)Pcr
, NewThread
->Teb
);
368 /* Set new TSS fields */
369 Pcr
->TSS
->Esp0
= (ULONG_PTR
)NewThread
->InitialStack
;
370 if (!((KeGetTrapFrame(NewThread
))->EFlags
& EFLAGS_V86_MASK
))
372 Pcr
->TSS
->Esp0
-= sizeof(KTRAP_FRAME
) - FIELD_OFFSET(KTRAP_FRAME
, V86Es
);
374 Pcr
->TSS
->Esp0
-= NPX_FRAME_LENGTH
;
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
)
402 HalRequestSoftwareInterrupt(APC_LEVEL
);
408 /* Return stating that no kernel APCs are pending*/
414 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame
,
415 IN ULONG_PTR OldThreadAndApcFlag
)
417 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
418 PKTHREAD OldThread
, NewThread
;
421 /* Save APC bypass disable */
422 SwitchFrame
->ApcBypassDisable
= OldThreadAndApcFlag
& 3;
423 SwitchFrame
->ExceptionList
= Pcr
->NtTib
.ExceptionList
;
425 /* Increase context switch count and check if tracing is enabled */
426 Pcr
->ContextSwitches
++;
427 if (Pcr
->PerfGlobalGroupMask
)
429 /* We don't support this yet on x86 either */
430 DPRINT1("WMI Tracing not supported\n");
434 /* Get thread pointers */
435 OldThread
= (PKTHREAD
)(OldThreadAndApcFlag
& ~3);
436 NewThread
= Pcr
->PrcbData
.CurrentThread
;
438 /* Get the old thread and set its kernel stack */
439 OldThread
->KernelStack
= SwitchFrame
;
441 /* ISRs can change FPU state, so disable interrupts while checking */
444 /* Get current and new CR0 and check if they've changed */
446 NewCr0
= NewThread
->NpxState
|
447 (Cr0
& ~(CR0_MP
| CR0_EM
| CR0_TS
)) |
448 KiGetThreadNpxArea(NewThread
)->Cr0NpxState
;
449 if (Cr0
!= NewCr0
) __writecr0(NewCr0
);
451 /* Now enable interrupts and do the switch */
453 KiSwitchThreads(OldThread
, NewThread
->KernelStack
);
458 KiDispatchInterrupt(VOID
)
460 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
461 PKPRCB Prcb
= &Pcr
->PrcbData
;
463 PKTHREAD NewThread
, OldThread
;
465 /* Disable interrupts */
468 /* Check for pending timers, pending DPCs, or pending ready threads */
469 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
470 (Prcb
->TimerRequest
) ||
471 (Prcb
->DeferredReadyListHead
.Next
))
473 /* Switch to safe execution context */
474 OldHandler
= Pcr
->NtTib
.ExceptionList
;
475 Pcr
->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
477 /* Retire DPCs while under the DPC stack */
478 KiRetireDpcListInDpcStack(Prcb
, Prcb
->DpcStack
);
480 /* Restore context */
481 Pcr
->NtTib
.ExceptionList
= OldHandler
;
484 /* Re-enable interrupts */
487 /* Check for quantum end */
488 if (Prcb
->QuantumEnd
)
490 /* Handle quantum end */
491 Prcb
->QuantumEnd
= FALSE
;
494 else if (Prcb
->NextThread
)
496 /* Capture current thread data */
497 OldThread
= Prcb
->CurrentThread
;
498 NewThread
= Prcb
->NextThread
;
500 /* Set new thread data */
501 Prcb
->NextThread
= NULL
;
502 Prcb
->CurrentThread
= NewThread
;
504 /* The thread is now running */
505 NewThread
->State
= Running
;
506 OldThread
->WaitReason
= WrDispatchInt
;
508 /* Make the old thread ready */
509 KxQueueReadyThread(OldThread
, Prcb
);
511 /* Swap to the new thread */
512 KiSwapContext(APC_LEVEL
, OldThread
);