mirror of
https://github.com/reactos/reactos.git
synced 2024-11-23 19:43:31 +08:00
518 lines
15 KiB
C
518 lines
15 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/ke/i386/thrdini.c
|
|
* PURPOSE: i386 Thread Context Creation
|
|
* PROGRAMMER: Alex Ionescu (alex@relsoft.net)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
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)
|
|
{
|
|
KeBugCheck(NO_USER_MODE_CONTEXT);
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
DECLSPEC_NORETURN
|
|
VOID
|
|
KiIdleLoop(VOID)
|
|
{
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
PKTHREAD OldThread, NewThread;
|
|
|
|
/* Now loop forever */
|
|
while (TRUE)
|
|
{
|
|
/* Start of the idle loop: disable interrupts */
|
|
_enable();
|
|
YieldProcessor();
|
|
YieldProcessor();
|
|
_disable();
|
|
|
|
/* 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 interrupts */
|
|
_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);
|
|
}
|
|
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)
|
|
{
|
|
if (NewProcess->LdtDescriptor.LimitLow)
|
|
{
|
|
KeSetGdtSelector(KGDT_LDT,
|
|
((PULONG)&NewProcess->LdtDescriptor)[0],
|
|
((PULONG)&NewProcess->LdtDescriptor)[1]);
|
|
Ke386SetLocalDescriptorTable(KGDT_LDT);
|
|
}
|
|
else
|
|
{
|
|
Ke386SetLocalDescriptorTable(0);
|
|
}
|
|
}
|
|
|
|
/* Switch address space and flush TLB */
|
|
__writecr3(NewProcess->DirectoryTableBase[0]);
|
|
}
|
|
|
|
/* Clear GS */
|
|
Ke386SetGs(0);
|
|
|
|
/* Set the TEB */
|
|
KiSetTebBase((PKPCR)Pcr, &NewThread->Teb->NtTib);
|
|
|
|
/* Set new TSS fields */
|
|
Pcr->TSS->Esp0 = (ULONG_PTR)NewThread->InitialStack;
|
|
if (!((KeGetTrapFrame(NewThread))->EFlags & EFLAGS_V86_MASK))
|
|
{
|
|
Pcr->TSS->Esp0 -= sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, V86Es);
|
|
}
|
|
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 */
|