[CMAKE]
[reactos.git] / ntoskrnl / ke / i386 / thrdini.c
1 /*
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)
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->DispatcherHeader.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 PKGDTENTRY GdtEntry;
336 PKTHREAD NewThread;
337 PKUINIT_FRAME InitFrame;
338
339 /* We are on the new thread stack now */
340 NewThread = Pcr->PrcbData.CurrentThread;
341
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)
346 {
347 /* Check if there is a different LDT */
348 if (*(PULONGLONG)&OldProcess->LdtDescriptor != *(PULONGLONG)&NewProcess->LdtDescriptor)
349 {
350 DPRINT1("LDT switch not implemented\n");
351 ASSERT(FALSE);
352 }
353
354 /* Switch address space and flush TLB */
355 __writecr3(NewProcess->DirectoryTableBase[0]);
356 }
357
358 /* Clear GS */
359 Ke386SetGs(0);
360
361 /* Set the TEB */
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);
367
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))
372 {
373 Pcr->TSS->Esp0 -= (FIELD_OFFSET(KTRAP_FRAME, V86Gs) - FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs));
374 }
375 Pcr->TSS->IoMapBase = NewProcess->IopmOffset;
376
377 /* Increase thread context switches */
378 NewThread->ContextSwitches++;
379
380 /* Load data from switch frame */
381 Pcr->NtTib.ExceptionList = SwitchFrame->ExceptionList;
382
383 /* DPCs shouldn't be active */
384 if (Pcr->PrcbData.DpcRoutineActive)
385 {
386 /* Crash the machine */
387 KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
388 (ULONG_PTR)OldThread,
389 (ULONG_PTR)NewThread,
390 (ULONG_PTR)OldThread->InitialStack,
391 0);
392 }
393
394 /* Kernel APCs may be pending */
395 if (NewThread->ApcState.KernelApcPending)
396 {
397 /* Are APCs enabled? */
398 if (!NewThread->SpecialApcDisable)
399 {
400 /* Request APC delivery */
401 if (!SwitchFrame->ApcBypassDisable) HalRequestSoftwareInterrupt(APC_LEVEL);
402 return TRUE;
403 }
404 }
405
406 /* Return */
407 return FALSE;
408 }
409
410 VOID
411 FASTCALL
412 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame,
413 IN ULONG_PTR OldThreadAndApcFlag)
414 {
415 PKIPCR Pcr = (PKIPCR)KeGetPcr();
416 PKTHREAD OldThread, NewThread;
417 ULONG Cr0, NewCr0;
418
419 /* Save APC bypass disable */
420 SwitchFrame->ApcBypassDisable = OldThreadAndApcFlag & 3;
421 SwitchFrame->ExceptionList = Pcr->NtTib.ExceptionList;
422
423 /* Increase context switch count and check if tracing is enabled */
424 Pcr->ContextSwitches++;
425 if (Pcr->PerfGlobalGroupMask)
426 {
427 /* We don't support this yet on x86 either */
428 DPRINT1("WMI Tracing not supported\n");
429 ASSERT(FALSE);
430 }
431
432 /* Get thread pointers */
433 OldThread = (PKTHREAD)(OldThreadAndApcFlag & ~3);
434 NewThread = Pcr->PrcbData.CurrentThread;
435
436 /* Get the old thread and set its kernel stack */
437 OldThread->KernelStack = SwitchFrame;
438
439 /* ISRs can change FPU state, so disable interrupts while checking */
440 _disable();
441
442 /* Get current and new CR0 and check if they've changed */
443 Cr0 = __readcr0();
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);
448
449 /* Now enable interrupts and do the switch */
450 _enable();
451 KiSwitchThreads(OldThread, NewThread->KernelStack);
452 }
453
454 VOID
455 NTAPI
456 KiDispatchInterrupt(VOID)
457 {
458 PKIPCR Pcr = (PKIPCR)KeGetPcr();
459 PKPRCB Prcb = &Pcr->PrcbData;
460 PVOID OldHandler;
461 PKTHREAD NewThread, OldThread;
462
463 /* Disable interrupts */
464 _disable();
465
466 /* Check for pending timers, pending DPCs, or pending ready threads */
467 if ((Prcb->DpcData[0].DpcQueueDepth) ||
468 (Prcb->TimerRequest) ||
469 (Prcb->DeferredReadyListHead.Next))
470 {
471 /* Switch to safe execution context */
472 OldHandler = Pcr->NtTib.ExceptionList;
473 Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
474
475 /* Retire DPCs while under the DPC stack */
476 KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
477
478 /* Restore context */
479 Pcr->NtTib.ExceptionList = OldHandler;
480 }
481
482 /* Re-enable interrupts */
483 _enable();
484
485 /* Check for quantum end */
486 if (Prcb->QuantumEnd)
487 {
488 /* Handle quantum end */
489 Prcb->QuantumEnd = FALSE;
490 KiQuantumEnd();
491 }
492 else if (Prcb->NextThread)
493 {
494 /* Capture current thread data */
495 OldThread = Prcb->CurrentThread;
496 NewThread = Prcb->NextThread;
497
498 /* Set new thread data */
499 Prcb->NextThread = NULL;
500 Prcb->CurrentThread = NewThread;
501
502 /* The thread is now running */
503 NewThread->State = Running;
504 OldThread->WaitReason = WrDispatchInt;
505
506 /* Make the old thread ready */
507 KxQueueReadyThread(OldThread, Prcb);
508
509 /* Swap to the new thread */
510 KiSwapContext(APC_LEVEL, OldThread);
511 }
512 }
513
514
515 /* EOF */