xref: /reactos/ntoskrnl/ke/i386/thrdini.c (revision 1734f297)
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         /* 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 : CONTEXT_FLOATING_POINT;
162 
163         /* Set the Thread's NPX State */
164         Thread->NpxState = NPX_STATE_NOT_LOADED;
165         Thread->Header.NpxIrql = PASSIVE_LEVEL;
166 
167         /* Disable any debug registers */
168         Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
169 
170         /* Setup the Trap Frame */
171         TrapFrame = &InitFrame->TrapFrame;
172 
173         /* Set up a trap frame from the context. */
174         KeContextToTrapFrame(Context,
175                              NULL,
176                              TrapFrame,
177                              Context->ContextFlags | ContextFlags,
178                              UserMode);
179 
180         /* Set SS, DS, ES's RPL Mask properly */
181         TrapFrame->HardwareSegSs |= RPL_MASK;
182         TrapFrame->SegDs |= RPL_MASK;
183         TrapFrame->SegEs |= RPL_MASK;
184         TrapFrame->Dr7 = 0;
185 
186         /* Set the debug mark */
187         TrapFrame->DbgArgMark = 0xBADB0D00;
188 
189         /* Set the previous mode as user */
190         TrapFrame->PreviousPreviousMode = UserMode;
191 
192         /* Terminate the Exception Handler List */
193         TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
194 
195         /* Setup the Stack for KiThreadStartup and Context Switching */
196         StartFrame = &InitFrame->StartFrame;
197         CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
198 
199         /* Tell the thread it will run in User Mode */
200         Thread->PreviousMode = UserMode;
201 
202         /* Tell KiThreadStartup of that too */
203         StartFrame->UserThread = TRUE;
204     }
205     else
206     {
207         /* Set up the Initial Frame for the system thread */
208         PKKINIT_FRAME InitFrame;
209         InitFrame = (PKKINIT_FRAME)((ULONG_PTR)Thread->InitialStack -
210                                     sizeof(KKINIT_FRAME));
211 
212         /* Setup the Fx Area */
213         FxSaveArea = &InitFrame->FxSaveArea;
214         RtlZeroMemory(FxSaveArea, sizeof(FX_SAVE_AREA));
215 
216         /* Check if we have Fxsr support */
217         if (KeI386FxsrPresent)
218         {
219             /* Set the stub FX area */
220             FxSaveArea->U.FxArea.ControlWord = 0x27F;
221             FxSaveArea->U.FxArea.MXCsr = 0x1F80;
222         }
223         else
224         {
225             /* Set the stub FN area */
226             FxSaveArea->U.FnArea.ControlWord = 0x27F;
227             FxSaveArea->U.FnArea.TagWord = -1;
228         }
229 
230         /* No NPX State */
231         Thread->NpxState = NPX_STATE_NOT_LOADED;
232 
233         /* Setup the Stack for KiThreadStartup and Context Switching */
234         StartFrame = &InitFrame->StartFrame;
235         CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
236 
237         /* Tell the thread it will run in Kernel Mode */
238         Thread->PreviousMode = KernelMode;
239 
240         /* Tell KiThreadStartup of that too */
241         StartFrame->UserThread = FALSE;
242     }
243 
244     /* Now setup the remaining data for KiThreadStartup */
245     StartFrame->StartContext = StartContext;
246     StartFrame->StartRoutine = StartRoutine;
247     StartFrame->SystemRoutine = SystemRoutine;
248 
249     /* And set up the Context Switch Frame */
250     CtxSwitchFrame->RetAddr = KiThreadStartup;
251     CtxSwitchFrame->ApcBypassDisable = TRUE;
252     CtxSwitchFrame->ExceptionList = EXCEPTION_CHAIN_END;
253 
254     /* Save back the new value of the kernel stack. */
255     Thread->KernelStack = (PVOID)CtxSwitchFrame;
256 }
257 
258 DECLSPEC_NORETURN
259 VOID
260 KiIdleLoop(VOID)
261 {
262     PKPRCB Prcb = KeGetCurrentPrcb();
263     PKTHREAD OldThread, NewThread;
264 
265     /* Now loop forever */
266     while (TRUE)
267     {
268         /* Start of the idle loop: disable interrupts */
269         _enable();
270         YieldProcessor();
271         YieldProcessor();
272         _disable();
273 
274         /* Check for pending timers, pending DPCs, or pending ready threads */
275         if ((Prcb->DpcData[0].DpcQueueDepth) ||
276             (Prcb->TimerRequest) ||
277             (Prcb->DeferredReadyListHead.Next))
278         {
279             /* Quiesce the DPC software interrupt */
280             HalClearSoftwareInterrupt(DISPATCH_LEVEL);
281 
282             /* Handle it */
283             KiRetireDpcList(Prcb);
284         }
285 
286         /* Check if a new thread is scheduled for execution */
287         if (Prcb->NextThread)
288         {
289             /* Enable interrupts */
290             _enable();
291 
292             /* Capture current thread data */
293             OldThread = Prcb->CurrentThread;
294             NewThread = Prcb->NextThread;
295 
296             /* Set new thread data */
297             Prcb->NextThread = NULL;
298             Prcb->CurrentThread = NewThread;
299 
300             /* The thread is now running */
301             NewThread->State = Running;
302 
303             /* Switch away from the idle thread */
304             KiSwapContext(APC_LEVEL, OldThread);
305         }
306         else
307         {
308             /* Continue staying idle. Note the HAL returns with interrupts on */
309             Prcb->PowerState.IdleFunction(&Prcb->PowerState);
310         }
311     }
312 }
313 
314 BOOLEAN
315 FASTCALL
316 KiSwapContextExit(IN PKTHREAD OldThread,
317                   IN PKSWITCHFRAME SwitchFrame)
318 {
319     PKIPCR Pcr = (PKIPCR)KeGetPcr();
320     PKPROCESS OldProcess, NewProcess;
321     PKTHREAD NewThread;
322 
323     /* We are on the new thread stack now */
324     NewThread = Pcr->PrcbData.CurrentThread;
325 
326     /* Now we are the new thread. Check if it's in a new process */
327     OldProcess = OldThread->ApcState.Process;
328     NewProcess = NewThread->ApcState.Process;
329     if (OldProcess != NewProcess)
330     {
331         /* Check if there is a different LDT */
332         if (*(PULONGLONG)&OldProcess->LdtDescriptor != *(PULONGLONG)&NewProcess->LdtDescriptor)
333         {
334             if (NewProcess->LdtDescriptor.LimitLow)
335             {
336                 KeSetGdtSelector(KGDT_LDT,
337                                  ((PULONG)&NewProcess->LdtDescriptor)[0],
338                                  ((PULONG)&NewProcess->LdtDescriptor)[1]);
339                 Ke386SetLocalDescriptorTable(KGDT_LDT);
340             }
341             else
342             {
343                 Ke386SetLocalDescriptorTable(0);
344             }
345         }
346 
347         /* Switch address space and flush TLB */
348         __writecr3(NewProcess->DirectoryTableBase[0]);
349     }
350 
351     /* Clear GS */
352     Ke386SetGs(0);
353 
354     /* Set the TEB */
355     KiSetTebBase((PKPCR)Pcr, &NewThread->Teb->NtTib);
356 
357     /* Set new TSS fields */
358     Pcr->TSS->Esp0 = (ULONG_PTR)NewThread->InitialStack;
359     if (!((KeGetTrapFrame(NewThread))->EFlags & EFLAGS_V86_MASK))
360     {
361         Pcr->TSS->Esp0 -= sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, V86Es);
362     }
363     Pcr->TSS->Esp0 -= NPX_FRAME_LENGTH;
364     Pcr->TSS->IoMapBase = NewProcess->IopmOffset;
365 
366     /* Increase thread context switches */
367     NewThread->ContextSwitches++;
368 
369     /* Load data from switch frame */
370     Pcr->NtTib.ExceptionList = SwitchFrame->ExceptionList;
371 
372     /* DPCs shouldn't be active */
373     if (Pcr->PrcbData.DpcRoutineActive)
374     {
375         /* Crash the machine */
376         KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
377                      (ULONG_PTR)OldThread,
378                      (ULONG_PTR)NewThread,
379                      (ULONG_PTR)OldThread->InitialStack,
380                      0);
381     }
382 
383     /* Kernel APCs may be pending */
384     if (NewThread->ApcState.KernelApcPending)
385     {
386         /* Are APCs enabled? */
387         if (!NewThread->SpecialApcDisable)
388         {
389             /* Request APC delivery */
390             if (SwitchFrame->ApcBypassDisable)
391                 HalRequestSoftwareInterrupt(APC_LEVEL);
392             else
393                 return TRUE;
394         }
395     }
396 
397     /* Return stating that no kernel APCs are pending*/
398     return FALSE;
399 }
400 
401 VOID
402 FASTCALL
403 KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame,
404                    IN ULONG_PTR OldThreadAndApcFlag)
405 {
406     PKIPCR Pcr = (PKIPCR)KeGetPcr();
407     PKTHREAD OldThread, NewThread;
408     ULONG Cr0, NewCr0;
409 
410     /* Save APC bypass disable */
411     SwitchFrame->ApcBypassDisable = OldThreadAndApcFlag & 3;
412     SwitchFrame->ExceptionList = Pcr->NtTib.ExceptionList;
413 
414     /* Increase context switch count and check if tracing is enabled */
415     Pcr->ContextSwitches++;
416     if (Pcr->PerfGlobalGroupMask)
417     {
418         /* We don't support this yet on x86 either */
419         DPRINT1("WMI Tracing not supported\n");
420         ASSERT(FALSE);
421     }
422 
423     /* Get thread pointers */
424     OldThread = (PKTHREAD)(OldThreadAndApcFlag & ~3);
425     NewThread = Pcr->PrcbData.CurrentThread;
426 
427     /* Get the old thread and set its kernel stack */
428     OldThread->KernelStack = SwitchFrame;
429 
430     /* Set swapbusy to false for the new thread */
431     NewThread->SwapBusy = FALSE;
432 
433     /* ISRs can change FPU state, so disable interrupts while checking */
434     _disable();
435 
436     /* Get current and new CR0 and check if they've changed */
437     Cr0 = __readcr0();
438     NewCr0 = NewThread->NpxState |
439              (Cr0 & ~(CR0_MP | CR0_EM | CR0_TS)) |
440              KiGetThreadNpxArea(NewThread)->Cr0NpxState;
441     if (Cr0 != NewCr0)  __writecr0(NewCr0);
442 
443     /* Now enable interrupts and do the switch */
444     _enable();
445     KiSwitchThreads(OldThread, NewThread->KernelStack);
446 }
447 
448 VOID
449 NTAPI
450 KiDispatchInterrupt(VOID)
451 {
452     PKIPCR Pcr = (PKIPCR)KeGetPcr();
453     PKPRCB Prcb = &Pcr->PrcbData;
454     PVOID OldHandler;
455     PKTHREAD NewThread, OldThread;
456 
457     /* Disable interrupts */
458     _disable();
459 
460     /* Check for pending timers, pending DPCs, or pending ready threads */
461     if ((Prcb->DpcData[0].DpcQueueDepth) ||
462         (Prcb->TimerRequest) ||
463         (Prcb->DeferredReadyListHead.Next))
464     {
465         /* Switch to safe execution context */
466         OldHandler = Pcr->NtTib.ExceptionList;
467         Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
468 
469         /* Retire DPCs while under the DPC stack */
470         KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
471 
472         /* Restore context */
473         Pcr->NtTib.ExceptionList = OldHandler;
474     }
475 
476     /* Re-enable interrupts */
477     _enable();
478 
479     /* Check for quantum end */
480     if (Prcb->QuantumEnd)
481     {
482         /* Handle quantum end */
483         Prcb->QuantumEnd = FALSE;
484         KiQuantumEnd();
485     }
486     else if (Prcb->NextThread)
487     {
488         /* Capture current thread data */
489         OldThread = Prcb->CurrentThread;
490         NewThread = Prcb->NextThread;
491 
492         /* Set new thread data */
493         Prcb->NextThread = NULL;
494         Prcb->CurrentThread = NewThread;
495 
496         /* The thread is now running */
497         NewThread->State = Running;
498         OldThread->WaitReason = WrDispatchInt;
499 
500         /* Make the old thread ready */
501         KxQueueReadyThread(OldThread, Prcb);
502 
503         /* Swap to the new thread */
504         KiSwapContext(APC_LEVEL, OldThread);
505     }
506 }
507 
508 
509 /* EOF */
510