[NTOS:KE] In KiExitV86Mode, restore KTSS::Esp0 to its standard value. CORE-16531
[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/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 -= sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, V86Es);
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 */