/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/i386/thread.c * PURPOSE: i386 Thread Context Creation * PROGRAMMER: Alex Ionescu (alex@relsoft.net) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include typedef struct _KSWITCHFRAME { PVOID ExceptionList; BOOLEAN ApcBypassDisable; PVOID RetAddr; } KSWITCHFRAME, *PKSWITCHFRAME; typedef struct _KSTART_FRAME { PKSYSTEM_ROUTINE SystemRoutine; PKSTART_ROUTINE StartRoutine; PVOID StartContext; BOOLEAN UserThread; } KSTART_FRAME, *PKSTART_FRAME; typedef struct _KUINIT_FRAME { KSWITCHFRAME CtxSwitchFrame; KSTART_FRAME StartFrame; KTRAP_FRAME TrapFrame; FX_SAVE_AREA FxSaveArea; } KUINIT_FRAME, *PKUINIT_FRAME; typedef struct _KKINIT_FRAME { KSWITCHFRAME CtxSwitchFrame; KSTART_FRAME StartFrame; FX_SAVE_AREA FxSaveArea; } KKINIT_FRAME, *PKKINIT_FRAME; VOID FASTCALL KiSwitchThreads( IN PKTHREAD OldThread, IN PKTHREAD NewThread ); VOID FASTCALL KiRetireDpcListInDpcStack( IN PKPRCB Prcb, IN PVOID DpcStack ); /* FUNCTIONS *****************************************************************/ VOID NTAPI KiThreadStartup(VOID) { PKTRAP_FRAME TrapFrame; PKSTART_FRAME StartFrame; PKUINIT_FRAME InitFrame; /* Get the start and trap frames */ InitFrame = KeGetCurrentThread()->KernelStack; StartFrame = &InitFrame->StartFrame; TrapFrame = &InitFrame->TrapFrame; /* Lower to APC level */ KfLowerIrql(APC_LEVEL); /* Call the system routine */ StartFrame->SystemRoutine(StartFrame->StartRoutine, StartFrame->StartContext); /* If we returned, we better be a user thread */ if (!StartFrame->UserThread) DbgBreakPoint(); /* Exit to user-mode */ KiServiceExit2(TrapFrame); } VOID NTAPI KiInitializeContextThread(IN PKTHREAD Thread, IN PKSYSTEM_ROUTINE SystemRoutine, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext, IN PCONTEXT ContextPointer) { PFX_SAVE_AREA FxSaveArea; PFXSAVE_FORMAT FxSaveFormat; PKSTART_FRAME StartFrame; PKSWITCHFRAME CtxSwitchFrame; PKTRAP_FRAME TrapFrame; CONTEXT LocalContext; PCONTEXT Context = NULL; ULONG ContextFlags; /* Check if this is a With-Context Thread */ if (ContextPointer) { /* Set up the Initial Frame */ PKUINIT_FRAME InitFrame; InitFrame = (PKUINIT_FRAME)((ULONG_PTR)Thread->InitialStack - sizeof(KUINIT_FRAME)); /* Copy over the context we got */ RtlCopyMemory(&LocalContext, ContextPointer, sizeof(CONTEXT)); Context = &LocalContext; ContextFlags = CONTEXT_CONTROL; /* Zero out the trap frame and save area */ RtlZeroMemory(&InitFrame->TrapFrame, KTRAP_FRAME_LENGTH + sizeof(FX_SAVE_AREA)); /* Setup the Fx Area */ FxSaveArea = &InitFrame->FxSaveArea; /* Check if we support FXsr */ if (KeI386FxsrPresent) { /* Get the FX Save Format Area */ FxSaveFormat = (PFXSAVE_FORMAT)Context->ExtendedRegisters; /* Set an initial state */ FxSaveFormat->ControlWord = 0x27F; FxSaveFormat->StatusWord = 0; FxSaveFormat->TagWord = 0; FxSaveFormat->ErrorOffset = 0; FxSaveFormat->ErrorSelector = 0; FxSaveFormat->DataOffset = 0; FxSaveFormat->DataSelector = 0; FxSaveFormat->MXCsr = 0x1F80; } else { /* Setup the regular save area */ Context->FloatSave.ControlWord = 0x27F; Context->FloatSave.StatusWord = 0; Context->FloatSave.TagWord = -1; Context->FloatSave.ErrorOffset = 0; Context->FloatSave.ErrorSelector = 0; Context->FloatSave.DataOffset =0; Context->FloatSave.DataSelector = 0; } /* Check if the CPU has NPX */ if (KeI386NpxPresent) { /* Set an intial NPX State */ Context->FloatSave.Cr0NpxState = 0; FxSaveArea->Cr0NpxState = 0; FxSaveArea->NpxSavedCpu = 0; /* Now set the context flags depending on XMM support */ ContextFlags |= (KeI386FxsrPresent) ? CONTEXT_EXTENDED_REGISTERS : CONTEXT_FLOATING_POINT; /* Set the Thread's NPX State */ Thread->NpxState = NPX_STATE_NOT_LOADED; Thread->Header.NpxIrql = PASSIVE_LEVEL; } else { /* We'll use emulation */ FxSaveArea->Cr0NpxState = CR0_EM; Thread->NpxState = NPX_STATE_NOT_LOADED &~ CR0_MP; } /* Disable any debug registers */ Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS; /* Setup the Trap Frame */ TrapFrame = &InitFrame->TrapFrame; /* Set up a trap frame from the context. */ KeContextToTrapFrame(Context, NULL, TrapFrame, Context->ContextFlags | ContextFlags, UserMode); /* Set SS, DS, ES's RPL Mask properly */ TrapFrame->HardwareSegSs |= RPL_MASK; TrapFrame->SegDs |= RPL_MASK; TrapFrame->SegEs |= RPL_MASK; TrapFrame->Dr7 = 0; /* Set the debug mark */ TrapFrame->DbgArgMark = 0xBADB0D00; /* Set the previous mode as user */ TrapFrame->PreviousPreviousMode = UserMode; /* Terminate the Exception Handler List */ TrapFrame->ExceptionList = EXCEPTION_CHAIN_END; /* Setup the Stack for KiThreadStartup and Context Switching */ StartFrame = &InitFrame->StartFrame; CtxSwitchFrame = &InitFrame->CtxSwitchFrame; /* Tell the thread it will run in User Mode */ Thread->PreviousMode = UserMode; /* Tell KiThreadStartup of that too */ StartFrame->UserThread = TRUE; } else { /* Set up the Initial Frame for the system thread */ PKKINIT_FRAME InitFrame; InitFrame = (PKKINIT_FRAME)((ULONG_PTR)Thread->InitialStack - sizeof(KKINIT_FRAME)); /* Setup the Fx Area */ FxSaveArea = &InitFrame->FxSaveArea; RtlZeroMemory(FxSaveArea, sizeof(FX_SAVE_AREA)); /* Check if we have Fxsr support */ if (KeI386FxsrPresent) { /* Set the stub FX area */ FxSaveArea->U.FxArea.ControlWord = 0x27F; FxSaveArea->U.FxArea.MXCsr = 0x1F80; } else { /* Set the stub FN area */ FxSaveArea->U.FnArea.ControlWord = 0x27F; FxSaveArea->U.FnArea.TagWord = -1; } /* No NPX State */ Thread->NpxState = NPX_STATE_NOT_LOADED; /* Setup the Stack for KiThreadStartup and Context Switching */ StartFrame = &InitFrame->StartFrame; CtxSwitchFrame = &InitFrame->CtxSwitchFrame; /* Tell the thread it will run in Kernel Mode */ Thread->PreviousMode = KernelMode; /* Tell KiThreadStartup of that too */ StartFrame->UserThread = FALSE; } /* Now setup the remaining data for KiThreadStartup */ StartFrame->StartContext = StartContext; StartFrame->StartRoutine = StartRoutine; StartFrame->SystemRoutine = SystemRoutine; /* And set up the Context Switch Frame */ CtxSwitchFrame->RetAddr = KiThreadStartup; CtxSwitchFrame->ApcBypassDisable = TRUE; CtxSwitchFrame->ExceptionList = EXCEPTION_CHAIN_END; /* Save back the new value of the kernel stack. */ Thread->KernelStack = (PVOID)CtxSwitchFrame; } VOID FASTCALL KiIdleLoop(VOID) { PKPRCB Prcb = KeGetCurrentPrcb(); PKTHREAD OldThread, NewThread; /* Initialize the idle loop: disable interrupts */ _enable(); YieldProcessor(); YieldProcessor(); _disable(); /* Now loop forever */ while (TRUE) { /* Check for pending timers, pending DPCs, or pending ready threads */ if ((Prcb->DpcData[0].DpcQueueDepth) || (Prcb->TimerRequest) || (Prcb->DeferredReadyListHead.Next)) { /* Quiesce the DPC software interrupt */ HalClearSoftwareInterrupt(DISPATCH_LEVEL); /* Handle it */ KiRetireDpcList(Prcb); } /* Check if a new thread is scheduled for execution */ if (Prcb->NextThread) { /* Enable interupts */ _enable(); /* Capture current thread data */ OldThread = Prcb->CurrentThread; NewThread = Prcb->NextThread; /* Set new thread data */ Prcb->NextThread = NULL; Prcb->CurrentThread = NewThread; /* The thread is now running */ NewThread->State = Running; /* Switch away from the idle thread */ KiSwapContext(APC_LEVEL, OldThread); /* We are back in the idle thread -- disable interrupts again */ _enable(); YieldProcessor(); YieldProcessor(); _disable(); } else { /* Continue staying idle. Note the HAL returns with interrupts on */ Prcb->PowerState.IdleFunction(&Prcb->PowerState); } } } BOOLEAN FASTCALL KiSwapContextExit(IN PKTHREAD OldThread, IN PKSWITCHFRAME SwitchFrame) { PKIPCR Pcr = (PKIPCR)KeGetPcr(); PKPROCESS OldProcess, NewProcess; PKTHREAD NewThread; /* We are on the new thread stack now */ NewThread = Pcr->PrcbData.CurrentThread; /* Now we are the new thread. Check if it's in a new process */ OldProcess = OldThread->ApcState.Process; NewProcess = NewThread->ApcState.Process; if (OldProcess != NewProcess) { /* Check if there is a different LDT */ if (*(PULONGLONG)&OldProcess->LdtDescriptor != *(PULONGLONG)&NewProcess->LdtDescriptor) { DPRINT1("LDT switch not implemented\n"); ASSERT(FALSE); } /* Switch address space and flush TLB */ __writecr3(NewProcess->DirectoryTableBase[0]); } /* Clear GS */ Ke386SetGs(0); /* Set the TEB */ KiSetTebBase((PKPCR)Pcr, NewThread->Teb); /* Set new TSS fields */ Pcr->TSS->Esp0 = (ULONG_PTR)NewThread->InitialStack; if (!((KeGetTrapFrame(NewThread))->EFlags & EFLAGS_V86_MASK)) { Pcr->TSS->Esp0 -= (FIELD_OFFSET(KTRAP_FRAME, V86Gs) - FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs)); } Pcr->TSS->Esp0 -= NPX_FRAME_LENGTH; Pcr->TSS->IoMapBase = NewProcess->IopmOffset; /* Increase thread context switches */ NewThread->ContextSwitches++; /* Load data from switch frame */ Pcr->NtTib.ExceptionList = SwitchFrame->ExceptionList; /* DPCs shouldn't be active */ if (Pcr->PrcbData.DpcRoutineActive) { /* Crash the machine */ KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC, (ULONG_PTR)OldThread, (ULONG_PTR)NewThread, (ULONG_PTR)OldThread->InitialStack, 0); } /* Kernel APCs may be pending */ if (NewThread->ApcState.KernelApcPending) { /* Are APCs enabled? */ if (!NewThread->SpecialApcDisable) { /* Request APC delivery */ if (SwitchFrame->ApcBypassDisable) HalRequestSoftwareInterrupt(APC_LEVEL); else return TRUE; } } /* Return stating that no kernel APCs are pending*/ return FALSE; } VOID FASTCALL KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame, IN ULONG_PTR OldThreadAndApcFlag) { PKIPCR Pcr = (PKIPCR)KeGetPcr(); PKTHREAD OldThread, NewThread; ULONG Cr0, NewCr0; /* Save APC bypass disable */ SwitchFrame->ApcBypassDisable = OldThreadAndApcFlag & 3; SwitchFrame->ExceptionList = Pcr->NtTib.ExceptionList; /* Increase context switch count and check if tracing is enabled */ Pcr->ContextSwitches++; if (Pcr->PerfGlobalGroupMask) { /* We don't support this yet on x86 either */ DPRINT1("WMI Tracing not supported\n"); ASSERT(FALSE); } /* Get thread pointers */ OldThread = (PKTHREAD)(OldThreadAndApcFlag & ~3); NewThread = Pcr->PrcbData.CurrentThread; /* Get the old thread and set its kernel stack */ OldThread->KernelStack = SwitchFrame; /* ISRs can change FPU state, so disable interrupts while checking */ _disable(); /* Get current and new CR0 and check if they've changed */ Cr0 = __readcr0(); NewCr0 = NewThread->NpxState | (Cr0 & ~(CR0_MP | CR0_EM | CR0_TS)) | KiGetThreadNpxArea(NewThread)->Cr0NpxState; if (Cr0 != NewCr0) __writecr0(NewCr0); /* Now enable interrupts and do the switch */ _enable(); KiSwitchThreads(OldThread, NewThread->KernelStack); } VOID NTAPI KiDispatchInterrupt(VOID) { PKIPCR Pcr = (PKIPCR)KeGetPcr(); PKPRCB Prcb = &Pcr->PrcbData; PVOID OldHandler; PKTHREAD NewThread, OldThread; /* Disable interrupts */ _disable(); /* Check for pending timers, pending DPCs, or pending ready threads */ if ((Prcb->DpcData[0].DpcQueueDepth) || (Prcb->TimerRequest) || (Prcb->DeferredReadyListHead.Next)) { /* Switch to safe execution context */ OldHandler = Pcr->NtTib.ExceptionList; Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END; /* Retire DPCs while under the DPC stack */ KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack); /* Restore context */ Pcr->NtTib.ExceptionList = OldHandler; } /* Re-enable interrupts */ _enable(); /* Check for quantum end */ if (Prcb->QuantumEnd) { /* Handle quantum end */ Prcb->QuantumEnd = FALSE; KiQuantumEnd(); } else if (Prcb->NextThread) { /* Capture current thread data */ OldThread = Prcb->CurrentThread; NewThread = Prcb->NextThread; /* Set new thread data */ Prcb->NextThread = NULL; Prcb->CurrentThread = NewThread; /* The thread is now running */ NewThread->State = Running; OldThread->WaitReason = WrDispatchInt; /* Make the old thread ready */ KxQueueReadyThread(OldThread, Prcb); /* Swap to the new thread */ KiSwapContext(APC_LEVEL, OldThread); } } /* EOF */