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