xref: /reactos/ntoskrnl/ke/i386/thrdini.c (revision ebaf247c)
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)
82     {
83         KeBugCheck(NO_USER_MODE_CONTEXT);
84     }
85 
86     /* Exit to user-mode */
87     KiServiceExit2(TrapFrame);
88 }
89 
90 VOID
91 NTAPI
92 KiInitializeContextThread(IN PKTHREAD Thread,
93                           IN PKSYSTEM_ROUTINE SystemRoutine,
94                           IN PKSTART_ROUTINE StartRoutine,
95                           IN PVOID StartContext,
96                           IN PCONTEXT ContextPointer)
97 {
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;
105     ULONG ContextFlags;
106 
107     /* Check if this is a With-Context Thread */
108     if (ContextPointer)
109     {
110         /* Set up the Initial Frame */
111         PKUINIT_FRAME InitFrame;
112         InitFrame = (PKUINIT_FRAME)((ULONG_PTR)Thread->InitialStack -
113                                     sizeof(KUINIT_FRAME));
114 
115         /* Copy over the context we got */
116         RtlCopyMemory(&LocalContext, ContextPointer, sizeof(CONTEXT));
117         Context = &LocalContext;
118         ContextFlags = CONTEXT_CONTROL;
119 
120         /* Zero out the trap frame and save area */
121         RtlZeroMemory(&InitFrame->TrapFrame,
122                       KTRAP_FRAME_LENGTH + sizeof(FX_SAVE_AREA));
123 
124         /* Setup the Fx Area */
125         FxSaveArea = &InitFrame->FxSaveArea;
126 
127         /* Check if we support FXsr */
128         if (KeI386FxsrPresent)
129         {
130             /* Get the FX Save Format Area */
131             FxSaveFormat = (PFXSAVE_FORMAT)Context->ExtendedRegisters;
132 
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;
142         }
143         else
144         {
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;
153         }
154 
155         /* Check if the CPU has NPX */
156         if (KeI386NpxPresent)
157         {
158             /* Set an intial NPX State */
159             Context->FloatSave.Cr0NpxState = 0;
160             FxSaveArea->Cr0NpxState = 0;
161             FxSaveArea->NpxSavedCpu = 0;
162 
163             /* Now set the context flags depending on XMM support */
164             ContextFlags |= (KeI386FxsrPresent) ? CONTEXT_EXTENDED_REGISTERS :
165                                                   CONTEXT_FLOATING_POINT;
166 
167             /* Set the Thread's NPX State */
168             Thread->NpxState = NPX_STATE_NOT_LOADED;
169             Thread->Header.NpxIrql = PASSIVE_LEVEL;
170         }
171         else
172         {
173             /* We'll use emulation */
174             FxSaveArea->Cr0NpxState = CR0_EM;
175             Thread->NpxState = NPX_STATE_NOT_LOADED &~ CR0_MP;
176         }
177 
178         /* Disable any debug registers */
179         Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
180 
181         /* Setup the Trap Frame */
182         TrapFrame = &InitFrame->TrapFrame;
183 
184         /* Set up a trap frame from the context. */
185         KeContextToTrapFrame(Context,
186                              NULL,
187                              TrapFrame,
188                              Context->ContextFlags | ContextFlags,
189                              UserMode);
190 
191         /* Set SS, DS, ES's RPL Mask properly */
192         TrapFrame->HardwareSegSs |= RPL_MASK;
193         TrapFrame->SegDs |= RPL_MASK;
194         TrapFrame->SegEs |= RPL_MASK;
195         TrapFrame->Dr7 = 0;
196 
197         /* Set the debug mark */
198         TrapFrame->DbgArgMark = 0xBADB0D00;
199 
200         /* Set the previous mode as user */
201         TrapFrame->PreviousPreviousMode = UserMode;
202 
203         /* Terminate the Exception Handler List */
204         TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
205 
206         /* Setup the Stack for KiThreadStartup and Context Switching */
207         StartFrame = &InitFrame->StartFrame;
208         CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
209 
210         /* Tell the thread it will run in User Mode */
211         Thread->PreviousMode = UserMode;
212 
213         /* Tell KiThreadStartup of that too */
214         StartFrame->UserThread = TRUE;
215     }
216     else
217     {
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));
222 
223         /* Setup the Fx Area */
224         FxSaveArea = &InitFrame->FxSaveArea;
225         RtlZeroMemory(FxSaveArea, sizeof(FX_SAVE_AREA));
226 
227         /* Check if we have Fxsr support */
228         if (KeI386FxsrPresent)
229         {
230             /* Set the stub FX area */
231             FxSaveArea->U.FxArea.ControlWord = 0x27F;
232             FxSaveArea->U.FxArea.MXCsr = 0x1F80;
233         }
234         else
235         {
236             /* Set the stub FN area */
237             FxSaveArea->U.FnArea.ControlWord = 0x27F;
238             FxSaveArea->U.FnArea.TagWord = -1;
239         }
240 
241         /* No NPX State */
242         Thread->NpxState = NPX_STATE_NOT_LOADED;
243 
244         /* Setup the Stack for KiThreadStartup and Context Switching */
245         StartFrame = &InitFrame->StartFrame;
246         CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
247 
248         /* Tell the thread it will run in Kernel Mode */
249         Thread->PreviousMode = KernelMode;
250 
251         /* Tell KiThreadStartup of that too */
252         StartFrame->UserThread = FALSE;
253     }
254 
255     /* Now setup the remaining data for KiThreadStartup */
256     StartFrame->StartContext = StartContext;
257     StartFrame->StartRoutine = StartRoutine;
258     StartFrame->SystemRoutine = SystemRoutine;
259 
260     /* And set up the Context Switch Frame */
261     CtxSwitchFrame->RetAddr = KiThreadStartup;
262     CtxSwitchFrame->ApcBypassDisable = TRUE;
263     CtxSwitchFrame->ExceptionList = EXCEPTION_CHAIN_END;
264 
265     /* Save back the new value of the kernel stack. */
266     Thread->KernelStack = (PVOID)CtxSwitchFrame;
267 }
268 
269 VOID
270 FASTCALL
271 KiIdleLoop(VOID)
272 {
273     PKPRCB Prcb = KeGetCurrentPrcb();
274     PKTHREAD OldThread, NewThread;
275 
276     /* Now loop forever */
277     while (TRUE)
278     {
279         /* Start of the idle loop: disable interrupts */
280         _enable();
281         YieldProcessor();
282         YieldProcessor();
283         _disable();
284 
285         /* Check for pending timers, pending DPCs, or pending ready threads */
286         if ((Prcb->DpcData[0].DpcQueueDepth) ||
287             (Prcb->TimerRequest) ||
288             (Prcb->DeferredReadyListHead.Next))
289         {
290             /* Quiesce the DPC software interrupt */
291             HalClearSoftwareInterrupt(DISPATCH_LEVEL);
292 
293             /* Handle it */
294             KiRetireDpcList(Prcb);
295         }
296 
297         /* Check if a new thread is scheduled for execution */
298         if (Prcb->NextThread)
299         {
300             /* Enable interrupts */
301             _enable();
302 
303             /* Capture current thread data */
304             OldThread = Prcb->CurrentThread;
305             NewThread = Prcb->NextThread;
306 
307             /* Set new thread data */
308             Prcb->NextThread = NULL;
309             Prcb->CurrentThread = NewThread;
310 
311             /* The thread is now running */
312             NewThread->State = Running;
313 
314             /* Switch away from the idle thread */
315             KiSwapContext(APC_LEVEL, OldThread);
316         }
317         else
318         {
319             /* Continue staying idle. Note the HAL returns with interrupts on */
320             Prcb->PowerState.IdleFunction(&Prcb->PowerState);
321         }
322     }
323 }
324 
325 BOOLEAN
326 FASTCALL
327 KiSwapContextExit(IN PKTHREAD OldThread,
328                   IN PKSWITCHFRAME SwitchFrame)
329 {
330     PKIPCR Pcr = (PKIPCR)KeGetPcr();
331     PKPROCESS OldProcess, NewProcess;
332     PKTHREAD NewThread;
333 
334     /* We are on the new thread stack now */
335     NewThread = Pcr->PrcbData.CurrentThread;
336 
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)
341     {
342         /* Check if there is a different LDT */
343         if (*(PULONGLONG)&OldProcess->LdtDescriptor != *(PULONGLONG)&NewProcess->LdtDescriptor)
344         {
345             if (NewProcess->LdtDescriptor.LimitLow)
346             {
347                 KeSetGdtSelector(KGDT_LDT,
348                                  ((PULONG)&NewProcess->LdtDescriptor)[0],
349                                  ((PULONG)&NewProcess->LdtDescriptor)[1]);
350                 Ke386SetLocalDescriptorTable(KGDT_LDT);
351             }
352             else
353             {
354                 Ke386SetLocalDescriptorTable(0);
355             }
356         }
357 
358         /* Switch address space and flush TLB */
359         __writecr3(NewProcess->DirectoryTableBase[0]);
360     }
361 
362     /* Clear GS */
363     Ke386SetGs(0);
364 
365     /* Set the TEB */
366     KiSetTebBase((PKPCR)Pcr, NewThread->Teb);
367 
368     /* Set new TSS fields */
369     Pcr->TSS->Esp0 = (ULONG_PTR)NewThread->InitialStack;
370     if (!((KeGetTrapFrame(NewThread))->EFlags & EFLAGS_V86_MASK))
371     {
372         Pcr->TSS->Esp0 -= (FIELD_OFFSET(KTRAP_FRAME, V86Gs) - FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs));
373     }
374     Pcr->TSS->Esp0 -= NPX_FRAME_LENGTH;
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)
402                 HalRequestSoftwareInterrupt(APC_LEVEL);
403             else
404                 return TRUE;
405         }
406     }
407 
408     /* Return stating that no kernel APCs are pending*/
409     return FALSE;
410 }
411 
412 VOID
413 FASTCALL
414 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame,
415                    IN ULONG_PTR OldThreadAndApcFlag)
416 {
417     PKIPCR Pcr = (PKIPCR)KeGetPcr();
418     PKTHREAD OldThread, NewThread;
419     ULONG Cr0, NewCr0;
420 
421     /* Save APC bypass disable */
422     SwitchFrame->ApcBypassDisable = OldThreadAndApcFlag & 3;
423     SwitchFrame->ExceptionList = Pcr->NtTib.ExceptionList;
424 
425     /* Increase context switch count and check if tracing is enabled */
426     Pcr->ContextSwitches++;
427     if (Pcr->PerfGlobalGroupMask)
428     {
429         /* We don't support this yet on x86 either */
430         DPRINT1("WMI Tracing not supported\n");
431         ASSERT(FALSE);
432     }
433 
434     /* Get thread pointers */
435     OldThread = (PKTHREAD)(OldThreadAndApcFlag & ~3);
436     NewThread = Pcr->PrcbData.CurrentThread;
437 
438     /* Get the old thread and set its kernel stack */
439     OldThread->KernelStack = SwitchFrame;
440 
441     /* ISRs can change FPU state, so disable interrupts while checking */
442     _disable();
443 
444     /* Get current and new CR0 and check if they've changed */
445     Cr0 = __readcr0();
446     NewCr0 = NewThread->NpxState |
447              (Cr0 & ~(CR0_MP | CR0_EM | CR0_TS)) |
448              KiGetThreadNpxArea(NewThread)->Cr0NpxState;
449     if (Cr0 != NewCr0)  __writecr0(NewCr0);
450 
451     /* Now enable interrupts and do the switch */
452     _enable();
453     KiSwitchThreads(OldThread, NewThread->KernelStack);
454 }
455 
456 VOID
457 NTAPI
458 KiDispatchInterrupt(VOID)
459 {
460     PKIPCR Pcr = (PKIPCR)KeGetPcr();
461     PKPRCB Prcb = &Pcr->PrcbData;
462     PVOID OldHandler;
463     PKTHREAD NewThread, OldThread;
464 
465     /* Disable interrupts */
466     _disable();
467 
468     /* Check for pending timers, pending DPCs, or pending ready threads */
469     if ((Prcb->DpcData[0].DpcQueueDepth) ||
470         (Prcb->TimerRequest) ||
471         (Prcb->DeferredReadyListHead.Next))
472     {
473         /* Switch to safe execution context */
474         OldHandler = Pcr->NtTib.ExceptionList;
475         Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
476 
477         /* Retire DPCs while under the DPC stack */
478         KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
479 
480         /* Restore context */
481         Pcr->NtTib.ExceptionList = OldHandler;
482     }
483 
484     /* Re-enable interrupts */
485     _enable();
486 
487     /* Check for quantum end */
488     if (Prcb->QuantumEnd)
489     {
490         /* Handle quantum end */
491         Prcb->QuantumEnd = FALSE;
492         KiQuantumEnd();
493     }
494     else if (Prcb->NextThread)
495     {
496         /* Capture current thread data */
497         OldThread = Prcb->CurrentThread;
498         NewThread = Prcb->NextThread;
499 
500         /* Set new thread data */
501         Prcb->NextThread = NULL;
502         Prcb->CurrentThread = NewThread;
503 
504         /* The thread is now running */
505         NewThread->State = Running;
506         OldThread->WaitReason = WrDispatchInt;
507 
508         /* Make the old thread ready */
509         KxQueueReadyThread(OldThread, Prcb);
510 
511         /* Swap to the new thread */
512         KiSwapContext(APC_LEVEL, OldThread);
513     }
514 }
515 
516 
517 /* EOF */
518