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