[NTOS] Improve the FILE header section. Brought to you by Adam Stachowicz. CORE-10114
[reactos.git] / reactos / ntoskrnl / ke / i386 / thrdini.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 typedef struct _KSWITCHFRAME
16 {
17 PVOID ExceptionList;
18 BOOLEAN ApcBypassDisable;
19 PVOID RetAddr;
20 } KSWITCHFRAME, *PKSWITCHFRAME;
21
22 typedef struct _KSTART_FRAME
23 {
24 PKSYSTEM_ROUTINE SystemRoutine;
25 PKSTART_ROUTINE StartRoutine;
26 PVOID StartContext;
27 BOOLEAN UserThread;
28 } KSTART_FRAME, *PKSTART_FRAME;
29
30 typedef struct _KUINIT_FRAME
31 {
32 KSWITCHFRAME CtxSwitchFrame;
33 KSTART_FRAME StartFrame;
34 KTRAP_FRAME TrapFrame;
35 FX_SAVE_AREA FxSaveArea;
36 } KUINIT_FRAME, *PKUINIT_FRAME;
37
38 typedef struct _KKINIT_FRAME
39 {
40 KSWITCHFRAME CtxSwitchFrame;
41 KSTART_FRAME StartFrame;
42 FX_SAVE_AREA FxSaveArea;
43 } KKINIT_FRAME, *PKKINIT_FRAME;
44
45 VOID
46 FASTCALL
47 KiSwitchThreads(
48 IN PKTHREAD OldThread,
49 IN PKTHREAD NewThread
50 );
51
52 VOID
53 FASTCALL
54 KiRetireDpcListInDpcStack(
55 IN PKPRCB Prcb,
56 IN PVOID DpcStack
57 );
58
59 /* FUNCTIONS *****************************************************************/
60
61 VOID
62 NTAPI
63 KiThreadStartup(VOID)
64 {
65 PKTRAP_FRAME TrapFrame;
66 PKSTART_FRAME StartFrame;
67 PKUINIT_FRAME InitFrame;
68
69 /* Get the start and trap frames */
70 InitFrame = KeGetCurrentThread()->KernelStack;
71 StartFrame = &InitFrame->StartFrame;
72 TrapFrame = &InitFrame->TrapFrame;
73
74 /* Lower to APC level */
75 KfLowerIrql(APC_LEVEL);
76
77 /* Call the system routine */
78 StartFrame->SystemRoutine(StartFrame->StartRoutine, StartFrame->StartContext);
79
80 /* If we returned, we better be a user thread */
81 if (!StartFrame->UserThread) DbgBreakPoint();
82
83 /* Exit to user-mode */
84 KiServiceExit2(TrapFrame);
85 }
86
87 VOID
88 NTAPI
89 KiInitializeContextThread(IN PKTHREAD Thread,
90 IN PKSYSTEM_ROUTINE SystemRoutine,
91 IN PKSTART_ROUTINE StartRoutine,
92 IN PVOID StartContext,
93 IN PCONTEXT ContextPointer)
94 {
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;
102 ULONG ContextFlags;
103
104 /* Check if this is a With-Context Thread */
105 if (ContextPointer)
106 {
107 /* Set up the Initial Frame */
108 PKUINIT_FRAME InitFrame;
109 InitFrame = (PKUINIT_FRAME)((ULONG_PTR)Thread->InitialStack -
110 sizeof(KUINIT_FRAME));
111
112 /* Copy over the context we got */
113 RtlCopyMemory(&LocalContext, ContextPointer, sizeof(CONTEXT));
114 Context = &LocalContext;
115 ContextFlags = CONTEXT_CONTROL;
116
117 /* Zero out the trap frame and save area */
118 RtlZeroMemory(&InitFrame->TrapFrame,
119 KTRAP_FRAME_LENGTH + sizeof(FX_SAVE_AREA));
120
121 /* Setup the Fx Area */
122 FxSaveArea = &InitFrame->FxSaveArea;
123
124 /* Check if we support FXsr */
125 if (KeI386FxsrPresent)
126 {
127 /* Get the FX Save Format Area */
128 FxSaveFormat = (PFXSAVE_FORMAT)Context->ExtendedRegisters;
129
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;
139 }
140 else
141 {
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;
150 }
151
152 /* Check if the CPU has NPX */
153 if (KeI386NpxPresent)
154 {
155 /* Set an intial NPX State */
156 Context->FloatSave.Cr0NpxState = 0;
157 FxSaveArea->Cr0NpxState = 0;
158 FxSaveArea->NpxSavedCpu = 0;
159
160 /* Now set the context flags depending on XMM support */
161 ContextFlags |= (KeI386FxsrPresent) ? CONTEXT_EXTENDED_REGISTERS :
162 CONTEXT_FLOATING_POINT;
163
164 /* Set the Thread's NPX State */
165 Thread->NpxState = NPX_STATE_NOT_LOADED;
166 Thread->Header.NpxIrql = PASSIVE_LEVEL;
167 }
168 else
169 {
170 /* We'll use emulation */
171 FxSaveArea->Cr0NpxState = CR0_EM;
172 Thread->NpxState = NPX_STATE_NOT_LOADED &~ CR0_MP;
173 }
174
175 /* Disable any debug registers */
176 Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
177
178 /* Setup the Trap Frame */
179 TrapFrame = &InitFrame->TrapFrame;
180
181 /* Set up a trap frame from the context. */
182 KeContextToTrapFrame(Context,
183 NULL,
184 TrapFrame,
185 Context->ContextFlags | ContextFlags,
186 UserMode);
187
188 /* Set SS, DS, ES's RPL Mask properly */
189 TrapFrame->HardwareSegSs |= RPL_MASK;
190 TrapFrame->SegDs |= RPL_MASK;
191 TrapFrame->SegEs |= RPL_MASK;
192 TrapFrame->Dr7 = 0;
193
194 /* Set the debug mark */
195 TrapFrame->DbgArgMark = 0xBADB0D00;
196
197 /* Set the previous mode as user */
198 TrapFrame->PreviousPreviousMode = UserMode;
199
200 /* Terminate the Exception Handler List */
201 TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
202
203 /* Setup the Stack for KiThreadStartup and Context Switching */
204 StartFrame = &InitFrame->StartFrame;
205 CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
206
207 /* Tell the thread it will run in User Mode */
208 Thread->PreviousMode = UserMode;
209
210 /* Tell KiThreadStartup of that too */
211 StartFrame->UserThread = TRUE;
212 }
213 else
214 {
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));
219
220 /* Setup the Fx Area */
221 FxSaveArea = &InitFrame->FxSaveArea;
222 RtlZeroMemory(FxSaveArea, sizeof(FX_SAVE_AREA));
223
224 /* Check if we have Fxsr support */
225 if (KeI386FxsrPresent)
226 {
227 /* Set the stub FX area */
228 FxSaveArea->U.FxArea.ControlWord = 0x27F;
229 FxSaveArea->U.FxArea.MXCsr = 0x1F80;
230 }
231 else
232 {
233 /* Set the stub FN area */
234 FxSaveArea->U.FnArea.ControlWord = 0x27F;
235 FxSaveArea->U.FnArea.TagWord = -1;
236 }
237
238 /* No NPX State */
239 Thread->NpxState = NPX_STATE_NOT_LOADED;
240
241 /* Setup the Stack for KiThreadStartup and Context Switching */
242 StartFrame = &InitFrame->StartFrame;
243 CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
244
245 /* Tell the thread it will run in Kernel Mode */
246 Thread->PreviousMode = KernelMode;
247
248 /* Tell KiThreadStartup of that too */
249 StartFrame->UserThread = FALSE;
250 }
251
252 /* Now setup the remaining data for KiThreadStartup */
253 StartFrame->StartContext = StartContext;
254 StartFrame->StartRoutine = StartRoutine;
255 StartFrame->SystemRoutine = SystemRoutine;
256
257 /* And set up the Context Switch Frame */
258 CtxSwitchFrame->RetAddr = KiThreadStartup;
259 CtxSwitchFrame->ApcBypassDisable = TRUE;
260 CtxSwitchFrame->ExceptionList = EXCEPTION_CHAIN_END;
261
262 /* Save back the new value of the kernel stack. */
263 Thread->KernelStack = (PVOID)CtxSwitchFrame;
264 }
265
266 VOID
267 FASTCALL
268 KiIdleLoop(VOID)
269 {
270 PKPRCB Prcb = KeGetCurrentPrcb();
271 PKTHREAD OldThread, NewThread;
272
273 /* Initialize the idle loop: disable interrupts */
274 _enable();
275 YieldProcessor();
276 YieldProcessor();
277 _disable();
278
279 /* Now loop forever */
280 while (TRUE)
281 {
282 /* Check for pending timers, pending DPCs, or pending ready threads */
283 if ((Prcb->DpcData[0].DpcQueueDepth) ||
284 (Prcb->TimerRequest) ||
285 (Prcb->DeferredReadyListHead.Next))
286 {
287 /* Quiesce the DPC software interrupt */
288 HalClearSoftwareInterrupt(DISPATCH_LEVEL);
289
290 /* Handle it */
291 KiRetireDpcList(Prcb);
292 }
293
294 /* Check if a new thread is scheduled for execution */
295 if (Prcb->NextThread)
296 {
297 /* Enable interupts */
298 _enable();
299
300 /* Capture current thread data */
301 OldThread = Prcb->CurrentThread;
302 NewThread = Prcb->NextThread;
303
304 /* Set new thread data */
305 Prcb->NextThread = NULL;
306 Prcb->CurrentThread = NewThread;
307
308 /* The thread is now running */
309 NewThread->State = Running;
310
311 /* Switch away from the idle thread */
312 KiSwapContext(APC_LEVEL, OldThread);
313
314 /* We are back in the idle thread -- disable interrupts again */
315 _enable();
316 YieldProcessor();
317 YieldProcessor();
318 _disable();
319 }
320 else
321 {
322 /* Continue staying idle. Note the HAL returns with interrupts on */
323 Prcb->PowerState.IdleFunction(&Prcb->PowerState);
324 }
325 }
326 }
327
328 BOOLEAN
329 FASTCALL
330 KiSwapContextExit(IN PKTHREAD OldThread,
331 IN PKSWITCHFRAME SwitchFrame)
332 {
333 PKIPCR Pcr = (PKIPCR)KeGetPcr();
334 PKPROCESS OldProcess, NewProcess;
335 PKTHREAD NewThread;
336
337 /* We are on the new thread stack now */
338 NewThread = Pcr->PrcbData.CurrentThread;
339
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)
344 {
345 /* Check if there is a different LDT */
346 if (*(PULONGLONG)&OldProcess->LdtDescriptor != *(PULONGLONG)&NewProcess->LdtDescriptor)
347 {
348 DPRINT1("LDT switch not implemented\n");
349 ASSERT(FALSE);
350 }
351
352 /* Switch address space and flush TLB */
353 __writecr3(NewProcess->DirectoryTableBase[0]);
354 }
355
356 /* Clear GS */
357 Ke386SetGs(0);
358
359 /* Set the TEB */
360 KiSetTebBase((PKPCR)Pcr, NewThread->Teb);
361
362 /* Set new TSS fields */
363 Pcr->TSS->Esp0 = (ULONG_PTR)NewThread->InitialStack;
364 if (!((KeGetTrapFrame(NewThread))->EFlags & EFLAGS_V86_MASK))
365 {
366 Pcr->TSS->Esp0 -= (FIELD_OFFSET(KTRAP_FRAME, V86Gs) - FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs));
367 }
368 Pcr->TSS->Esp0 -= NPX_FRAME_LENGTH;
369 Pcr->TSS->IoMapBase = NewProcess->IopmOffset;
370
371 /* Increase thread context switches */
372 NewThread->ContextSwitches++;
373
374 /* Load data from switch frame */
375 Pcr->NtTib.ExceptionList = SwitchFrame->ExceptionList;
376
377 /* DPCs shouldn't be active */
378 if (Pcr->PrcbData.DpcRoutineActive)
379 {
380 /* Crash the machine */
381 KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
382 (ULONG_PTR)OldThread,
383 (ULONG_PTR)NewThread,
384 (ULONG_PTR)OldThread->InitialStack,
385 0);
386 }
387
388 /* Kernel APCs may be pending */
389 if (NewThread->ApcState.KernelApcPending)
390 {
391 /* Are APCs enabled? */
392 if (!NewThread->SpecialApcDisable)
393 {
394 /* Request APC delivery */
395 if (SwitchFrame->ApcBypassDisable)
396 HalRequestSoftwareInterrupt(APC_LEVEL);
397 else
398 return TRUE;
399 }
400 }
401
402 /* Return stating that no kernel APCs are pending*/
403 return FALSE;
404 }
405
406 VOID
407 FASTCALL
408 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame,
409 IN ULONG_PTR OldThreadAndApcFlag)
410 {
411 PKIPCR Pcr = (PKIPCR)KeGetPcr();
412 PKTHREAD OldThread, NewThread;
413 ULONG Cr0, NewCr0;
414
415 /* Save APC bypass disable */
416 SwitchFrame->ApcBypassDisable = OldThreadAndApcFlag & 3;
417 SwitchFrame->ExceptionList = Pcr->NtTib.ExceptionList;
418
419 /* Increase context switch count and check if tracing is enabled */
420 Pcr->ContextSwitches++;
421 if (Pcr->PerfGlobalGroupMask)
422 {
423 /* We don't support this yet on x86 either */
424 DPRINT1("WMI Tracing not supported\n");
425 ASSERT(FALSE);
426 }
427
428 /* Get thread pointers */
429 OldThread = (PKTHREAD)(OldThreadAndApcFlag & ~3);
430 NewThread = Pcr->PrcbData.CurrentThread;
431
432 /* Get the old thread and set its kernel stack */
433 OldThread->KernelStack = SwitchFrame;
434
435 /* ISRs can change FPU state, so disable interrupts while checking */
436 _disable();
437
438 /* Get current and new CR0 and check if they've changed */
439 Cr0 = __readcr0();
440 NewCr0 = NewThread->NpxState |
441 (Cr0 & ~(CR0_MP | CR0_EM | CR0_TS)) |
442 KiGetThreadNpxArea(NewThread)->Cr0NpxState;
443 if (Cr0 != NewCr0) __writecr0(NewCr0);
444
445 /* Now enable interrupts and do the switch */
446 _enable();
447 KiSwitchThreads(OldThread, NewThread->KernelStack);
448 }
449
450 VOID
451 NTAPI
452 KiDispatchInterrupt(VOID)
453 {
454 PKIPCR Pcr = (PKIPCR)KeGetPcr();
455 PKPRCB Prcb = &Pcr->PrcbData;
456 PVOID OldHandler;
457 PKTHREAD NewThread, OldThread;
458
459 /* Disable interrupts */
460 _disable();
461
462 /* Check for pending timers, pending DPCs, or pending ready threads */
463 if ((Prcb->DpcData[0].DpcQueueDepth) ||
464 (Prcb->TimerRequest) ||
465 (Prcb->DeferredReadyListHead.Next))
466 {
467 /* Switch to safe execution context */
468 OldHandler = Pcr->NtTib.ExceptionList;
469 Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
470
471 /* Retire DPCs while under the DPC stack */
472 KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
473
474 /* Restore context */
475 Pcr->NtTib.ExceptionList = OldHandler;
476 }
477
478 /* Re-enable interrupts */
479 _enable();
480
481 /* Check for quantum end */
482 if (Prcb->QuantumEnd)
483 {
484 /* Handle quantum end */
485 Prcb->QuantumEnd = FALSE;
486 KiQuantumEnd();
487 }
488 else if (Prcb->NextThread)
489 {
490 /* Capture current thread data */
491 OldThread = Prcb->CurrentThread;
492 NewThread = Prcb->NextThread;
493
494 /* Set new thread data */
495 Prcb->NextThread = NULL;
496 Prcb->CurrentThread = NewThread;
497
498 /* The thread is now running */
499 NewThread->State = Running;
500 OldThread->WaitReason = WrDispatchInt;
501
502 /* Make the old thread ready */
503 KxQueueReadyThread(OldThread, Prcb);
504
505 /* Swap to the new thread */
506 KiSwapContext(APC_LEVEL, OldThread);
507 }
508 }
509
510
511 /* EOF */