xref: /reactos/ntoskrnl/ke/i386/thrdini.c (revision c2c66aff)
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     /* Now loop forever */
274     while (TRUE)
275     {
276         /* Start of the idle loop: disable interrupts */
277         _enable();
278         YieldProcessor();
279         YieldProcessor();
280         _disable();
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 interrupts */
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         else
315         {
316             /* Continue staying idle. Note the HAL returns with interrupts on */
317             Prcb->PowerState.IdleFunction(&Prcb->PowerState);
318         }
319     }
320 }
321 
322 BOOLEAN
323 FASTCALL
324 KiSwapContextExit(IN PKTHREAD OldThread,
325                   IN PKSWITCHFRAME SwitchFrame)
326 {
327     PKIPCR Pcr = (PKIPCR)KeGetPcr();
328     PKPROCESS OldProcess, NewProcess;
329     PKTHREAD NewThread;
330 
331     /* We are on the new thread stack now */
332     NewThread = Pcr->PrcbData.CurrentThread;
333 
334     /* Now we are the new thread. Check if it's in a new process */
335     OldProcess = OldThread->ApcState.Process;
336     NewProcess = NewThread->ApcState.Process;
337     if (OldProcess != NewProcess)
338     {
339         /* Check if there is a different LDT */
340         if (*(PULONGLONG)&OldProcess->LdtDescriptor != *(PULONGLONG)&NewProcess->LdtDescriptor)
341         {
342             if (NewProcess->LdtDescriptor.LimitLow)
343             {
344                 KeSetGdtSelector(KGDT_LDT,
345                                  ((PULONG)&NewProcess->LdtDescriptor)[0],
346                                  ((PULONG)&NewProcess->LdtDescriptor)[1]);
347                 Ke386SetLocalDescriptorTable(KGDT_LDT);
348             }
349             else
350             {
351                 Ke386SetLocalDescriptorTable(0);
352             }
353         }
354 
355         /* Switch address space and flush TLB */
356         __writecr3(NewProcess->DirectoryTableBase[0]);
357     }
358 
359     /* Clear GS */
360     Ke386SetGs(0);
361 
362     /* Set the TEB */
363     KiSetTebBase((PKPCR)Pcr, NewThread->Teb);
364 
365     /* Set new TSS fields */
366     Pcr->TSS->Esp0 = (ULONG_PTR)NewThread->InitialStack;
367     if (!((KeGetTrapFrame(NewThread))->EFlags & EFLAGS_V86_MASK))
368     {
369         Pcr->TSS->Esp0 -= (FIELD_OFFSET(KTRAP_FRAME, V86Gs) - FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs));
370     }
371     Pcr->TSS->Esp0 -= NPX_FRAME_LENGTH;
372     Pcr->TSS->IoMapBase = NewProcess->IopmOffset;
373 
374     /* Increase thread context switches */
375     NewThread->ContextSwitches++;
376 
377     /* Load data from switch frame */
378     Pcr->NtTib.ExceptionList = SwitchFrame->ExceptionList;
379 
380     /* DPCs shouldn't be active */
381     if (Pcr->PrcbData.DpcRoutineActive)
382     {
383         /* Crash the machine */
384         KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
385                      (ULONG_PTR)OldThread,
386                      (ULONG_PTR)NewThread,
387                      (ULONG_PTR)OldThread->InitialStack,
388                      0);
389     }
390 
391     /* Kernel APCs may be pending */
392     if (NewThread->ApcState.KernelApcPending)
393     {
394         /* Are APCs enabled? */
395         if (!NewThread->SpecialApcDisable)
396         {
397             /* Request APC delivery */
398             if (SwitchFrame->ApcBypassDisable)
399                 HalRequestSoftwareInterrupt(APC_LEVEL);
400             else
401                 return TRUE;
402         }
403     }
404 
405     /* Return stating that no kernel APCs are pending*/
406     return FALSE;
407 }
408 
409 VOID
410 FASTCALL
411 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame,
412                    IN ULONG_PTR OldThreadAndApcFlag)
413 {
414     PKIPCR Pcr = (PKIPCR)KeGetPcr();
415     PKTHREAD OldThread, NewThread;
416     ULONG Cr0, NewCr0;
417 
418     /* Save APC bypass disable */
419     SwitchFrame->ApcBypassDisable = OldThreadAndApcFlag & 3;
420     SwitchFrame->ExceptionList = Pcr->NtTib.ExceptionList;
421 
422     /* Increase context switch count and check if tracing is enabled */
423     Pcr->ContextSwitches++;
424     if (Pcr->PerfGlobalGroupMask)
425     {
426         /* We don't support this yet on x86 either */
427         DPRINT1("WMI Tracing not supported\n");
428         ASSERT(FALSE);
429     }
430 
431     /* Get thread pointers */
432     OldThread = (PKTHREAD)(OldThreadAndApcFlag & ~3);
433     NewThread = Pcr->PrcbData.CurrentThread;
434 
435     /* Get the old thread and set its kernel stack */
436     OldThread->KernelStack = SwitchFrame;
437 
438     /* ISRs can change FPU state, so disable interrupts while checking */
439     _disable();
440 
441     /* Get current and new CR0 and check if they've changed */
442     Cr0 = __readcr0();
443     NewCr0 = NewThread->NpxState |
444              (Cr0 & ~(CR0_MP | CR0_EM | CR0_TS)) |
445              KiGetThreadNpxArea(NewThread)->Cr0NpxState;
446     if (Cr0 != NewCr0)  __writecr0(NewCr0);
447 
448     /* Now enable interrupts and do the switch */
449     _enable();
450     KiSwitchThreads(OldThread, NewThread->KernelStack);
451 }
452 
453 VOID
454 NTAPI
455 KiDispatchInterrupt(VOID)
456 {
457     PKIPCR Pcr = (PKIPCR)KeGetPcr();
458     PKPRCB Prcb = &Pcr->PrcbData;
459     PVOID OldHandler;
460     PKTHREAD NewThread, OldThread;
461 
462     /* Disable interrupts */
463     _disable();
464 
465     /* Check for pending timers, pending DPCs, or pending ready threads */
466     if ((Prcb->DpcData[0].DpcQueueDepth) ||
467         (Prcb->TimerRequest) ||
468         (Prcb->DeferredReadyListHead.Next))
469     {
470         /* Switch to safe execution context */
471         OldHandler = Pcr->NtTib.ExceptionList;
472         Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
473 
474         /* Retire DPCs while under the DPC stack */
475         KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
476 
477         /* Restore context */
478         Pcr->NtTib.ExceptionList = OldHandler;
479     }
480 
481     /* Re-enable interrupts */
482     _enable();
483 
484     /* Check for quantum end */
485     if (Prcb->QuantumEnd)
486     {
487         /* Handle quantum end */
488         Prcb->QuantumEnd = FALSE;
489         KiQuantumEnd();
490     }
491     else if (Prcb->NextThread)
492     {
493         /* Capture current thread data */
494         OldThread = Prcb->CurrentThread;
495         NewThread = Prcb->NextThread;
496 
497         /* Set new thread data */
498         Prcb->NextThread = NULL;
499         Prcb->CurrentThread = NewThread;
500 
501         /* The thread is now running */
502         NewThread->State = Running;
503         OldThread->WaitReason = WrDispatchInt;
504 
505         /* Make the old thread ready */
506         KxQueueReadyThread(OldThread, Prcb);
507 
508         /* Swap to the new thread */
509         KiSwapContext(APC_LEVEL, OldThread);
510     }
511 }
512 
513 
514 /* EOF */
515