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