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
) 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
->Header
.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 /* We are on the new thread stack now */
338 NewThread
= Pcr
->PrcbData
.CurrentThread
;
340 /* Now we are the new thread. Check if it's in a new process */
341 OldProcess
= OldThread
->ApcState
.Process
;
342 NewProcess
= NewThread
->ApcState
.Process
;
343 if (OldProcess
!= NewProcess
)
345 /* Check if there is a different LDT */
346 if (*(PULONGLONG
)&OldProcess
->LdtDescriptor
!= *(PULONGLONG
)&NewProcess
->LdtDescriptor
)
348 DPRINT1("LDT switch not implemented\n");
352 /* Switch address space and flush TLB */
353 __writecr3(NewProcess
->DirectoryTableBase
[0]);
360 KiSetTebBase((PKPCR
)Pcr
, NewThread
->Teb
);
362 /* Set new TSS fields */
363 Pcr
->TSS
->Esp0
= (ULONG_PTR
)NewThread
->InitialStack
;
364 if (!((KeGetTrapFrame(NewThread
))->EFlags
& EFLAGS_V86_MASK
))
366 Pcr
->TSS
->Esp0
-= (FIELD_OFFSET(KTRAP_FRAME
, V86Gs
) - FIELD_OFFSET(KTRAP_FRAME
, HardwareSegSs
));
368 Pcr
->TSS
->Esp0
-= NPX_FRAME_LENGTH
;
369 Pcr
->TSS
->IoMapBase
= NewProcess
->IopmOffset
;
371 /* Increase thread context switches */
372 NewThread
->ContextSwitches
++;
374 /* Load data from switch frame */
375 Pcr
->NtTib
.ExceptionList
= SwitchFrame
->ExceptionList
;
377 /* DPCs shouldn't be active */
378 if (Pcr
->PrcbData
.DpcRoutineActive
)
380 /* Crash the machine */
381 KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC
,
382 (ULONG_PTR
)OldThread
,
383 (ULONG_PTR
)NewThread
,
384 (ULONG_PTR
)OldThread
->InitialStack
,
388 /* Kernel APCs may be pending */
389 if (NewThread
->ApcState
.KernelApcPending
)
391 /* Are APCs enabled? */
392 if (!NewThread
->SpecialApcDisable
)
394 /* Request APC delivery */
395 if (SwitchFrame
->ApcBypassDisable
)
396 HalRequestSoftwareInterrupt(APC_LEVEL
);
402 /* Return stating that no kernel APCs are pending*/
408 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame
,
409 IN ULONG_PTR OldThreadAndApcFlag
)
411 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
412 PKTHREAD OldThread
, NewThread
;
415 /* Save APC bypass disable */
416 SwitchFrame
->ApcBypassDisable
= OldThreadAndApcFlag
& 3;
417 SwitchFrame
->ExceptionList
= Pcr
->NtTib
.ExceptionList
;
419 /* Increase context switch count and check if tracing is enabled */
420 Pcr
->ContextSwitches
++;
421 if (Pcr
->PerfGlobalGroupMask
)
423 /* We don't support this yet on x86 either */
424 DPRINT1("WMI Tracing not supported\n");
428 /* Get thread pointers */
429 OldThread
= (PKTHREAD
)(OldThreadAndApcFlag
& ~3);
430 NewThread
= Pcr
->PrcbData
.CurrentThread
;
432 /* Get the old thread and set its kernel stack */
433 OldThread
->KernelStack
= SwitchFrame
;
435 /* ISRs can change FPU state, so disable interrupts while checking */
438 /* Get current and new CR0 and check if they've changed */
440 NewCr0
= NewThread
->NpxState
|
441 (Cr0
& ~(CR0_MP
| CR0_EM
| CR0_TS
)) |
442 KiGetThreadNpxArea(NewThread
)->Cr0NpxState
;
443 if (Cr0
!= NewCr0
) __writecr0(NewCr0
);
445 /* Now enable interrupts and do the switch */
447 KiSwitchThreads(OldThread
, NewThread
->KernelStack
);
452 KiDispatchInterrupt(VOID
)
454 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
455 PKPRCB Prcb
= &Pcr
->PrcbData
;
457 PKTHREAD NewThread
, OldThread
;
459 /* Disable interrupts */
462 /* Check for pending timers, pending DPCs, or pending ready threads */
463 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
464 (Prcb
->TimerRequest
) ||
465 (Prcb
->DeferredReadyListHead
.Next
))
467 /* Switch to safe execution context */
468 OldHandler
= Pcr
->NtTib
.ExceptionList
;
469 Pcr
->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
471 /* Retire DPCs while under the DPC stack */
472 KiRetireDpcListInDpcStack(Prcb
, Prcb
->DpcStack
);
474 /* Restore context */
475 Pcr
->NtTib
.ExceptionList
= OldHandler
;
478 /* Re-enable interrupts */
481 /* Check for quantum end */
482 if (Prcb
->QuantumEnd
)
484 /* Handle quantum end */
485 Prcb
->QuantumEnd
= FALSE
;
488 else if (Prcb
->NextThread
)
490 /* Capture current thread data */
491 OldThread
= Prcb
->CurrentThread
;
492 NewThread
= Prcb
->NextThread
;
494 /* Set new thread data */
495 Prcb
->NextThread
= NULL
;
496 Prcb
->CurrentThread
= NewThread
;
498 /* The thread is now running */
499 NewThread
->State
= Running
;
500 OldThread
->WaitReason
= WrDispatchInt
;
502 /* Make the old thread ready */
503 KxQueueReadyThread(OldThread
, Prcb
);
505 /* Swap to the new thread */
506 KiSwapContext(APC_LEVEL
, OldThread
);