[NTOS:KE/x64] Handle NMI vs swapgs race condition
[reactos.git] / ntoskrnl / ke / amd64 / thrdini.c
index 058f34d..3185de2 100644 (file)
@@ -1,9 +1,10 @@
 /*
  * 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)
+ * FILE:            ntoskrnl/ke/amd64/thrdini.c
+ * PURPOSE:         amd64 Thread Context Creation
+ * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
+ *                  Alex Ionescu (alex@relsoft.net)
  */
 
 /* INCLUDES ******************************************************************/
 #define NDEBUG
 #include <debug.h>
 
+extern void KiInvalidSystemThreadStartupExit(void);
+extern void KiUserThreadStartupExit(void);
+extern void KiServiceExit3(void);
+
 typedef struct _KUINIT_FRAME
 {
     KSWITCH_FRAME CtxSwitchFrame;
     KSTART_FRAME StartFrame;
+    KEXCEPTION_FRAME ExceptionFrame;
     KTRAP_FRAME TrapFrame;
     //FX_SAVE_AREA FxSaveArea;
 } KUINIT_FRAME, *PKUINIT_FRAME;
@@ -35,74 +41,53 @@ KiInitializeContextThread(IN PKTHREAD Thread,
                            IN PKSYSTEM_ROUTINE SystemRoutine,
                            IN PKSTART_ROUTINE StartRoutine,
                            IN PVOID StartContext,
-                           IN PCONTEXT ContextPointer)
+                           IN PCONTEXT Context)
 {
     //PFX_SAVE_AREA FxSaveArea;
     //PFXSAVE_FORMAT FxSaveFormat;
     PKSTART_FRAME StartFrame;
     PKSWITCH_FRAME CtxSwitchFrame;
     PKTRAP_FRAME TrapFrame;
-    CONTEXT LocalContext;
-    PCONTEXT Context = NULL;
     ULONG ContextFlags;
 
     /* Check if this is a With-Context Thread */
-    if (ContextPointer)
+    if (Context)
     {
-        /* 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);
-
-        /* Setup the Fx Area */
-        //FxSaveArea = &InitFrame->FxSaveArea;
-
-//            /* 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;
-
-        /* 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 up the Initial Frame */
+        InitFrame = ((PKUINIT_FRAME)Thread->InitialStack) - 1;
+        StartFrame = &InitFrame->StartFrame;
+        CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
+
+        /* Save back the new value of the kernel stack. */
+        Thread->KernelStack = (PVOID)InitFrame;
+
+        /* Tell the thread it will run in User Mode */
+        Thread->PreviousMode = UserMode;
+
+        // FIXME Setup the Fx Area
 
         /* Set the Thread's NPX State */
         Thread->NpxState = 0xA;
         Thread->Header.NpxIrql = PASSIVE_LEVEL;
 
-        /* Disable any debug regiseters */
-        Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
+        /* Make sure, we have control registers, disable debug registers */
+        ASSERT((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL);
+        ContextFlags = Context->ContextFlags & ~CONTEXT_DEBUG_REGISTERS;
 
         /* Setup the Trap Frame */
         TrapFrame = &InitFrame->TrapFrame;
 
+        /* Zero out the trap frame */
+        RtlZeroMemory(TrapFrame, sizeof(KTRAP_FRAME));
+        RtlZeroMemory(&InitFrame->ExceptionFrame, sizeof(KEXCEPTION_FRAME));
+
         /* Set up a trap frame from the context. */
         KeContextToTrapFrame(Context,
-                             NULL,
+                             &InitFrame->ExceptionFrame,
                              TrapFrame,
-                             Context->ContextFlags | ContextFlags,
+                             CONTEXT_AMD64 | ContextFlags,
                              UserMode);
 
         /* Set SS, DS, ES's RPL Mask properly */
@@ -117,60 +102,115 @@ KiInitializeContextThread(IN PKTHREAD Thread,
         /* Terminate the Exception Handler List */
         TrapFrame->ExceptionFrame = 0;
 
-        /* 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;
+        /* KiThreadStartup returns to KiUserThreadStartupExit */
+        StartFrame->Return = (ULONG64)KiUserThreadStartupExit;
 
-        /* Tell KiThreadStartup of that too */
-//        StartFrame->UserThread = TRUE;
+        /* KiUserThreadStartupExit returns to KiServiceExit3 */
+        InitFrame->ExceptionFrame.Return = (ULONG64)KiServiceExit3;
     }
     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));
+        /* Set up the Initial Frame for the system thread */
+        InitFrame = ((PKKINIT_FRAME)Thread->InitialStack) - 1;
+        StartFrame = &InitFrame->StartFrame;
+        CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
 
-        /* Check if we have Fxsr support */
-        DPRINT1("FxsrPresent but did nothing\n");
-//        /* Set the stub FX area */
-//        FxSaveArea->U.FxArea.ControlWord = 0x27F;
-//        FxSaveArea->U.FxArea.MXCsr = 0x1F80;
+        /* Save back the new value of the kernel stack. */
+        Thread->KernelStack = (PVOID)InitFrame;
+
+        /* Tell the thread it will run in Kernel Mode */
+        Thread->PreviousMode = KernelMode;
+
+        // FIXME Setup the Fx Area
 
         /* No NPX State */
         Thread->NpxState = 0xA;
 
-        /* Setup the Stack for KiThreadStartup and Context Switching */
-        StartFrame = &InitFrame->StartFrame;
-        CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
+        /* This must never return! */
+        StartFrame->Return = (ULONG64)KiInvalidSystemThreadStartupExit;
+    }
 
-        /* Tell the thread it will run in Kernel Mode */
-        Thread->PreviousMode = KernelMode;
+    /* Set up the Context Switch Frame */
+    CtxSwitchFrame->Return = (ULONG64)KiThreadStartup;
+    CtxSwitchFrame->ApcBypass = TRUE;
+
+    StartFrame->P1Home = (ULONG64)StartRoutine;
+    StartFrame->P2Home = (ULONG64)StartContext;
+    StartFrame->P3Home = 0;
+    StartFrame->P4Home = (ULONG64)SystemRoutine;
+    StartFrame->Reserved = 0;
+}
+
+BOOLEAN
+KiSwapContextResume(
+    _In_ BOOLEAN ApcBypass,
+    _In_ PKTHREAD OldThread,
+    _In_ PKTHREAD NewThread)
+{
+    PKIPCR Pcr = (PKIPCR)KeGetPcr();
+    PKPROCESS OldProcess, NewProcess;
 
-        /* Tell KiThreadStartup of that too */
-//        StartFrame->UserThread = FALSE;
+    /* Setup ring 0 stack pointer */
+    Pcr->TssBase->Rsp0 = (ULONG64)NewThread->InitialStack; // FIXME: NPX save area?
+    Pcr->Prcb.RspBase = Pcr->TssBase->Rsp0;
+
+    /* 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)
+    {
+        /* Switch address space and flush TLB */
+        __writecr3(NewProcess->DirectoryTableBase[0]);
+
+        /* Set new TSS fields */
+        //Pcr->TssBase->IoMapBase = NewProcess->IopmOffset;
     }
 
-    /* Now setup the remaining data for KiThreadStartup */
-//    StartFrame->StartContext = StartContext;
-//    StartFrame->StartRoutine = StartRoutine;
-//    StartFrame->SystemRoutine = SystemRoutine;
+    /* Set TEB pointer and GS base */
+    Pcr->NtTib.Self = (PVOID)NewThread->Teb;
+    if (NewThread->Teb)
+    {
+       /* This will switch the usermode gs */
+       __writemsr(MSR_GS_SWAP, (ULONG64)NewThread->Teb);
+    }
 
-    /* And set up the Context Switch Frame */
-//    CtxSwitchFrame->RetAddr = KiThreadStartup;
-//    CtxSwitchFrame->ApcBypassDisable = TRUE;
-//    CtxSwitchFrame->ExceptionList = EXCEPTION_CHAIN_END;;
+    /* Increase context switch count */
+    Pcr->ContextSwitches++;
+    NewThread->ContextSwitches++;
 
-    /* Save back the new value of the kernel stack. */
-    Thread->KernelStack = (PVOID)CtxSwitchFrame;
+    /* DPCs shouldn't be active */
+    if (Pcr->Prcb.DpcRoutineActive)
+    {
+        /* Crash the machine */
+        KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
+                     (ULONG_PTR)OldThread,
+                     (ULONG_PTR)NewThread,
+                     (ULONG_PTR)OldThread->InitialStack,
+                     0);
+    }
+
+    /* Old thread os no longer busy */
+    OldThread->SwapBusy = FALSE;
+
+    /* Kernel APCs may be pending */
+    if (NewThread->ApcState.KernelApcPending)
+    {
+        /* Are APCs enabled? */
+        if ((NewThread->SpecialApcDisable == 0) &&
+            (ApcBypass == 0))
+        {
+            /* Return TRUE to indicate that we want APCs to be delivered */
+            return TRUE;
+        }
+
+        /* Request an APC interrupt to be delivered later */
+        HalRequestSoftwareInterrupt(APC_LEVEL);
+    }
 
+    /* Return stating that no kernel APCs are pending*/
+    return FALSE;
 }
 
 /* EOF */