- KdDebuggerNotPresent should be FALSE by default.
[reactos.git] / reactos / ntoskrnl / ke / i386 / kiinit.c
index 8e4f811..79061fd 100644 (file)
@@ -20,6 +20,293 @@ KSPIN_LOCK Ki486CompatibilityLock;
 
 /* FUNCTIONS *****************************************************************/
 
+VOID
+NTAPI
+KiInitMachineDependent(VOID)
+{
+    ULONG Protect;
+    ULONG CpuCount;
+    BOOLEAN FbCaching = FALSE;
+    NTSTATUS Status;
+    ULONG ReturnLength;
+    ULONG i, Affinity, Sample = 0;
+    PFX_SAVE_AREA FxSaveArea;
+    ULONG MXCsrMask = 0xFFBF;
+    ULONG Dummy[4];
+    KI_SAMPLE_MAP Samples[4];
+    PKI_SAMPLE_MAP CurrentSample = Samples;
+
+    /* Check for large page support */
+    if (KeFeatureBits & KF_LARGE_PAGE)
+    {
+        /* FIXME: Support this */
+        DPRINT1("Large Page support detected but not yet taken advantage of!\n");
+    }
+
+    /* Check for global page support */
+    if (KeFeatureBits & KF_GLOBAL_PAGE)
+    {
+        /* Do an IPI to enable it on all CPUs */
+        CpuCount = KeNumberProcessors;
+        KeIpiGenericCall(Ki386EnableGlobalPage, (ULONG_PTR)&CpuCount);
+    }
+
+    /* Check for PAT and/or MTRR support */
+    if (KeFeatureBits & (KF_PAT | KF_MTRR))
+    {
+        /* Query the HAL to make sure we can use it */
+        Status = HalQuerySystemInformation(HalFrameBufferCachingInformation,
+                                           sizeof(BOOLEAN),
+                                           &FbCaching,
+                                           &ReturnLength);
+        if ((NT_SUCCESS(Status)) && (FbCaching))
+        {
+            /* We can't, disable it */
+            KeFeatureBits &= ~(KF_PAT | KF_MTRR);
+        }
+    }
+
+    /* Check for PAT support and enable it */
+    if (KeFeatureBits & KF_PAT) KiInitializePAT();
+
+    /* Assume no errata for now */
+    SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = 0;
+
+    /* Check if we have an NPX */
+    if (KeI386NpxPresent)
+    {
+        /* Loop every CPU */
+        i = KeActiveProcessors;
+        for (Affinity = 1; i; Affinity <<= 1)
+        {
+            /* Check if this is part of the set */
+            if (i & Affinity)
+            {
+                /* Run on this CPU */
+                i &= ~Affinity;
+                KeSetSystemAffinityThread(Affinity);
+
+                /* Detect FPU errata */
+                if (KiIsNpxErrataPresent())
+                {
+                    /* Disable NPX support */
+                    KeI386NpxPresent = FALSE;
+                    SharedUserData->
+                        ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] =
+                        TRUE;
+                    break;
+                }
+            }
+        }
+    }
+
+    /* If there's no NPX, then we're emulating the FPU */
+    SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] =
+        !KeI386NpxPresent;
+
+    /* Check if there's no NPX, so that we can disable associated features */
+    if (!KeI386NpxPresent)
+    {
+        /* Remove NPX-related bits */
+        KeFeatureBits &= ~(KF_XMMI64 | KF_XMMI | KF_FXSR | KF_MMX);
+
+        /* Disable kernel flags */
+        KeI386FxsrPresent = KeI386XMMIPresent = FALSE;
+
+        /* Disable processor features that might've been set until now */
+        SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] =
+        SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE]   =
+        SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE]     =
+        SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE]    =
+        SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] = 0;
+    }
+
+    /* Check for CR4 support */
+    if (KeFeatureBits & KF_CR4)
+    {
+        /* Do an IPI call to enable the Debug Exceptions */
+        CpuCount = KeNumberProcessors;
+        KeIpiGenericCall(Ki386EnableDE, (ULONG_PTR)&CpuCount);
+    }
+
+    /* Check if FXSR was found */
+    if (KeFeatureBits & KF_FXSR)
+    {
+        /* Do an IPI call to enable the FXSR */
+        CpuCount = KeNumberProcessors;
+        KeIpiGenericCall(Ki386EnableFxsr, (ULONG_PTR)&CpuCount);
+
+        /* Check if XMM was found too */
+        if (KeFeatureBits & KF_XMMI)
+        {
+            /* Do an IPI call to enable XMMI exceptions */
+            CpuCount = KeNumberProcessors;
+            KeIpiGenericCall(Ki386EnableXMMIExceptions, (ULONG_PTR)&CpuCount);
+
+            /* FIXME: Implement and enable XMM Page Zeroing for Mm */
+
+            /* Patch the RtlPrefetchMemoryNonTemporal routine to enable it */
+            Protect = MmGetPageProtect(NULL, RtlPrefetchMemoryNonTemporal);
+            MmSetPageProtect(NULL,
+                             RtlPrefetchMemoryNonTemporal,
+                             Protect | PAGE_IS_WRITABLE);
+            *(PCHAR)RtlPrefetchMemoryNonTemporal = 0x90;
+            MmSetPageProtect(NULL, RtlPrefetchMemoryNonTemporal, Protect);
+        }
+    }
+
+    /* Check for, and enable SYSENTER support */
+    KiRestoreFastSyscallReturnState();
+
+    /* Loop every CPU */
+    i = KeActiveProcessors;
+    for (Affinity = 1; i; Affinity <<= 1)
+    {
+        /* Check if this is part of the set */
+        if (i & Affinity)
+        {
+            /* Run on this CPU */
+            i &= ~Affinity;
+            KeSetSystemAffinityThread(Affinity);
+
+            /* Reset MHz to 0 for this CPU */
+            KeGetCurrentPrcb()->MHz = 0;
+
+            /* Check if we can use RDTSC */
+            if (KeFeatureBits & KF_RDTSC)
+            {
+                /* Start sampling loop */
+                for (;;)
+                {
+                    /* Do a dummy CPUID to start the sample */
+                    CPUID(Dummy, 0);
+
+                    /* Fill out the starting data */
+                    CurrentSample->PerfStart = KeQueryPerformanceCounter(NULL);
+                    CurrentSample->TSCStart = __rdtsc();
+                    CurrentSample->PerfFreq.QuadPart = -50000;
+
+                    /* Sleep for this sample */
+                    KeDelayExecutionThread(KernelMode,
+                                           FALSE,
+                                           &CurrentSample->PerfFreq);
+
+                    /* Do another dummy CPUID */
+                    CPUID(Dummy, 0);
+
+                    /* Fill out the ending data */
+                    CurrentSample->PerfEnd =
+                        KeQueryPerformanceCounter(&CurrentSample->PerfFreq);
+                    CurrentSample->TSCEnd = __rdtsc();
+
+                    /* Calculate the differences */
+                    CurrentSample->PerfDelta = CurrentSample->PerfEnd.QuadPart -
+                                               CurrentSample->PerfStart.QuadPart;
+                    CurrentSample->TSCDelta = CurrentSample->TSCEnd -
+                                              CurrentSample->TSCStart;
+
+                    /* Compute CPU Speed */
+                    CurrentSample->MHz = ((CurrentSample->TSCDelta *
+                                           CurrentSample->PerfFreq.QuadPart +
+                                           500000) /
+                                          (CurrentSample->PerfDelta * 1000000));
+
+                    /* Check if this isn't the first sample */
+                    if (Sample)
+                    {
+                        /* Check if we got a good precision within 1MHz */
+                        if ((CurrentSample->MHz == CurrentSample[-1].MHz) ||
+                            (CurrentSample->MHz == CurrentSample[-1].MHz + 1) ||
+                            (CurrentSample->MHz == CurrentSample[-1].MHz - 1))
+                        {
+                            /* We did, stop sampling */
+                            break;
+                        }
+                    }
+
+                    /* Move on */
+                    CurrentSample++;
+                    Sample++;
+                }
+
+                /* Save the CPU Speed */
+                KeGetCurrentPrcb()->MHz = CurrentSample[-1].MHz;
+            }
+
+            /* Check if we have MTRR */
+            if (KeFeatureBits & KF_MTRR)
+            {
+                /* Then manually initialize MTRR for the CPU */
+                KiInitializeMTRR((BOOLEAN)i ? FALSE : TRUE);
+            }
+
+            /* Check if we have AMD MTRR and initialize it for the CPU */
+            if (KeFeatureBits & KF_AMDK6MTRR) KiAmdK6InitializeMTRR();
+
+            /* Check if this is a buggy Pentium and apply the fixup if so */
+            if (KiI386PentiumLockErrataPresent) KiI386PentiumLockErrataFixup();
+
+            /* Check if the CPU supports FXSR */
+            if (KeFeatureBits & KF_FXSR)
+            {
+                /* Get the current thread NPX state */
+                FxSaveArea = (PVOID)
+                             ((ULONG_PTR)KeGetCurrentThread()->InitialStack -
+                             NPX_FRAME_LENGTH);
+
+                /* Clear initial MXCsr mask */
+                FxSaveArea->U.FxArea.MXCsrMask = 0;
+
+                /* Save the current NPX State */
+#ifdef __GNUC__
+                asm volatile("fxsave %0\n\t" : "=m" (*FxSaveArea));
+#else
+                __asm fxsave [FxSaveArea]
+#endif
+                /* Check if the current mask doesn't match the reserved bits */
+                if (FxSaveArea->U.FxArea.MXCsrMask != 0)
+                {
+                    /* Then use whatever it's holding */
+                    MXCsrMask = FxSaveArea->U.FxArea.MXCsrMask;
+                }
+
+                /* Check if nobody set the kernel-wide mask */
+                if (!KiMXCsrMask)
+                {
+                    /* Then use the one we calculated above */
+                    KiMXCsrMask = MXCsrMask;
+                }
+                else
+                {
+                    /* Was it set to the same value we found now? */
+                    if (KiMXCsrMask != MXCsrMask)
+                    {
+                        /* No, something is definitely wrong */
+                        KEBUGCHECKEX(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED,
+                                     KF_FXSR,
+                                     KiMXCsrMask,
+                                     MXCsrMask,
+                                     0);
+                    }
+                }
+
+                /* Now set the kernel mask */
+                KiMXCsrMask &= MXCsrMask;
+            }
+        }
+    }
+
+    /* Return affinity back to where it was */
+    KeRevertToUserAffinityThread();
+
+    /* NT allows limiting the duration of an ISR with a registry key */
+    if (KiTimeLimitIsrMicroseconds)
+    {
+        /* FIXME: TODO */
+        DPRINT1("ISR Time Limit not yet supported\n");
+    }
+}
+
 VOID
 NTAPI
 KiInitializePcr(IN ULONG ProcessorNumber,
@@ -34,7 +321,7 @@ KiInitializePcr(IN ULONG ProcessorNumber,
     Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
     Pcr->NtTib.StackBase = 0;
     Pcr->NtTib.StackLimit = 0;
-    Pcr->NtTib.Self = 0;
+    Pcr->NtTib.Self = NULL;
 
     /* Set the Current Thread */
     Pcr->PrcbData.CurrentThread = IdleThread;
@@ -53,6 +340,12 @@ KiInitializePcr(IN ULONG ProcessorNumber,
 
     /* Set the Build Type */
     Pcr->PrcbData.BuildType = 0;
+#ifndef CONFIG_SMP
+    Pcr->PrcbData.BuildType |= PRCB_BUILD_UNIPROCESSOR;
+#endif
+#ifdef DBG
+    Pcr->PrcbData.BuildType |= PRCB_BUILD_DEBUG;
+#endif
 
     /* Set the Processor Number and current Processor Mask */
     Pcr->PrcbData.Number = (UCHAR)ProcessorNumber;
@@ -68,7 +361,11 @@ KiInitializePcr(IN ULONG ProcessorNumber,
     Pcr->GDT = (PVOID)Gdt;
     Pcr->IDT = Idt;
     Pcr->TSS = Tss;
+    Pcr->TssCopy = Tss;
     Pcr->PrcbData.DpcStack = DpcStack;
+
+    /* Setup the processor set */
+    Pcr->PrcbData.MultiThreadProcessorSet = Pcr->PrcbData.SetMember;
 }
 
 VOID
@@ -84,6 +381,8 @@ KiInitializeKernel(IN PKPROCESS InitProcess,
     ULONG FeatureBits;
     LARGE_INTEGER PageDirectory;
     PVOID DpcStack;
+    ULONG NXSupportPolicy;
+    ULONG Vendor[3];
 
     /* Detect and set the CPU Type */
     KiSetProcessorType();
@@ -103,6 +402,36 @@ KiInitializeKernel(IN PKPROCESS InitProcess,
     /* Get the processor features for the CPU */
     FeatureBits = KiGetFeatureBits();
 
+    /* Set the default NX policy (opt-in) */
+    NXSupportPolicy = NX_SUPPORT_POLICY_OPTIN;
+
+    /* Check if NPX is always on */
+    if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=ALWAYSON"))
+    {
+        /* Set it always on */
+        NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSON;
+        FeatureBits |= KF_NX_ENABLED;
+    }
+    else if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=OPTOUT"))
+    {
+        /* Set it in opt-out mode */
+        NXSupportPolicy = NX_SUPPORT_POLICY_OPTOUT;
+        FeatureBits |= KF_NX_ENABLED;
+    }
+    else if ((strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=OPTIN")) ||
+             (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE")))
+    {
+        /* Set the feature bits */
+        FeatureBits |= KF_NX_ENABLED;
+    }
+    else if ((strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=ALWAYSOFF")) ||
+             (strstr(KeLoaderBlock->LoadOptions, "EXECUTE")))
+    {
+        /* Set disabled mode */
+        NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSOFF;
+        FeatureBits |= KF_NX_DISABLED;
+    }
+
     /* Save feature bits */
     Prcb->FeatureBits = FeatureBits;
 
@@ -127,18 +456,32 @@ KiInitializeKernel(IN PKPROCESS InitProcess,
         KeI386NpxPresent = NpxPresent;
         KeI386CpuType = Prcb->CpuType;
         KeI386CpuStep = Prcb->CpuStep;
-        KeProcessorArchitecture = 0;
+        KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
         KeProcessorLevel = (USHORT)Prcb->CpuType;
         if (Prcb->CpuID) KeProcessorRevision = Prcb->CpuStep;
         KeFeatureBits = FeatureBits;
         KeI386FxsrPresent = (KeFeatureBits & KF_FXSR) ? TRUE : FALSE;
         KeI386XMMIPresent = (KeFeatureBits & KF_XMMI) ? TRUE : FALSE;
 
+        /* Detect 8-byte compare exchange support */
+        if (!(KeFeatureBits & KF_CMPXCHG8B))
+        {
+            /* Copy the vendor string */
+            RtlCopyMemory(Vendor, Prcb->VendorString, sizeof(Vendor));
+
+            /* Bugcheck the system. Windows *requires* this */
+            KeBugCheckEx(0x5D,
+                         (1 << 24 ) | (Prcb->CpuType << 16) | Prcb->CpuStep,
+                         Vendor[0],
+                         Vendor[1],
+                         Vendor[2]);
+        }
+
         /* Set the current MP Master KPRCB to the Boot PRCB */
         Prcb->MultiThreadSetMaster = Prcb;
 
         /* Lower to APC_LEVEL */
-        KfLowerIrql(APC_LEVEL);
+        KeLowerIrql(APC_LEVEL);
 
         /* Initialize some spinlocks */
         KeInitializeSpinLock(&KiFreezeExecutionLock);
@@ -163,17 +506,6 @@ KiInitializeKernel(IN PKPROCESS InitProcess,
         DPRINT1("SMP Boot support not yet present\n");
     }
 
-    /* Initialize Kernel Memory Address Space */
-    MmInit1(FirstKrnlPhysAddr,
-            LastKrnlPhysAddr,
-            LastKernelAddress,
-            (PADDRESS_RANGE)&KeMemoryMap,
-            KeMemoryMapRangeCount,
-            4096);
-
-    /* Sets up the Text Sections of the Kernel and HAL for debugging */
-    LdrInit1();
-
     /* Setup the Idle Thread */
     KeInitializeThread(InitProcess,
                        InitThread,
@@ -193,6 +525,34 @@ KiInitializeKernel(IN PKPROCESS InitProcess,
     /* HACK for MmUpdatePageDir */
     ((PETHREAD)InitThread)->ThreadsProcess = (PEPROCESS)InitProcess;
 
+    /* Initialize Kernel Memory Address Space */
+    MmInit1(MmFreeLdrFirstKrnlPhysAddr,
+            MmFreeLdrLastKrnlPhysAddr,
+            MmFreeLdrLastKernelAddress,
+            NULL,
+            0,
+            4096);
+
+    /* Sets up the Text Sections of the Kernel and HAL for debugging */
+    LdrInit1();
+
+    /* Set the NX Support policy */
+    SharedUserData->NXSupportPolicy = (UCHAR)NXSupportPolicy;
+
+    /* Set basic CPU Features that user mode can read */
+    SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
+        (KeFeatureBits & KF_MMX) ? TRUE: FALSE;
+    SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
+        (KeFeatureBits & KF_CMPXCHG8B) ? TRUE: FALSE;
+    SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
+        ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI)) ? TRUE: FALSE;
+    SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
+        ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI64)) ? TRUE: FALSE;
+    SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
+        (KeFeatureBits & KF_3DNOW) ? TRUE: FALSE;
+    SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
+        (KeFeatureBits & KF_RDTSC) ? TRUE: FALSE;
+
     /* Set up the thread-related fields in the PRCB */
     Prcb->CurrentThread = InitThread;
     Prcb->NextThread = NULL;
@@ -237,7 +597,9 @@ KiInitializeKernel(IN PKPROCESS InitProcess,
     KeSetPriorityThread(InitThread, 0);
 
     /* If there's no thread scheduled, put this CPU in the Idle summary */
+    KiAcquirePrcbLock(Prcb);
     if (!Prcb->NextThread) KiIdleSummary |= 1 << Number;
+    KiReleasePrcbLock(Prcb);
 
     /* Raise back to HIGH_LEVEL and clear the PRCB for the loader block */
     KfRaiseIrql(HIGH_LEVEL);
@@ -253,7 +615,7 @@ KiGetMachineBootPointers(IN PKGDTENTRY *Gdt,
 {
     KDESCRIPTOR GdtDescriptor, IdtDescriptor;
     KGDTENTRY TssSelector, PcrSelector;
-    ULONG Tr, Fs;
+    USHORT Tr, Fs;
 
     /* Get GDT and IDT descriptors */
     Ke386GetGlobalDescriptorTable(GdtDescriptor);
@@ -264,7 +626,7 @@ KiGetMachineBootPointers(IN PKGDTENTRY *Gdt,
     *Idt = (PKIDTENTRY)IdtDescriptor.Base;
 
     /* Get TSS and FS Selectors */
-    Ke386GetTr(&Tr);
+    Ke386GetTr(Tr);
     if (Tr != KGDT_TSS) Tr = KGDT_TSS; // FIXME: HACKHACK
     Fs = Ke386GetFs();
 
@@ -348,6 +710,9 @@ KiSystemStartup(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
     __writefsdword(KPCR_DR6, 0);
     __writefsdword(KPCR_DR7, 0);
 
+    /* Setup the IDT */
+    KeInitExceptions();
+
     /* Load Ring 3 selectors for DS/ES */
     Ke386SetDs(KGDT_R3_DATA | RPL_MASK);
     Ke386SetEs(KGDT_R3_DATA | RPL_MASK);
@@ -388,18 +753,24 @@ AppCpuInit:
 
     /* Align stack and make space for the trap frame and NPX frame */
     InitialStack &= ~KTRAP_FRAME_ALIGN;
+#ifdef __GNUC__
     __asm__ __volatile__("movl %0,%%esp" : :"r" (InitialStack));
     __asm__ __volatile__("subl %0,%%esp" : :"r" (NPX_FRAME_LENGTH +
                                                  KTRAP_FRAME_LENGTH +
                                                  KTRAP_FRAME_ALIGN));
     __asm__ __volatile__("push %0" : :"r" (CR0_EM + CR0_TS + CR0_MP));
+#else
+    __asm mov esp, InitialStack;
+    __asm sub esp, NPX_FRAME_LENGTH + KTRAP_FRAME_ALIGN + KTRAP_FRAME_LENGTH;
+    __asm push CR0_EM + CR0_TS + CR0_MP
+#endif
 
     /* Call main kernel initialization */
     KiInitializeKernel(&KiInitialProcess.Pcb,
                        InitialThread,
                        (PVOID)InitialStack,
                        (PKPRCB)__readfsdword(KPCR_PRCB),
-                       Cpu,
+                       (CCHAR)Cpu,
                        KeLoaderBlock);
 
     /* Set the priority of this thread to 0 */
@@ -412,12 +783,6 @@ AppCpuInit:
     /* Set the right wait IRQL */
     KeGetCurrentThread()->WaitIrql = DISPATCH_LEVEL;
 
-    /* Set idle thread as running on UP builds */
-#ifndef CONFIG_SMP
-    KeGetCurrentThread()->State = Running;
-#endif
-
     /* Jump into the idle loop */
     KiIdleLoop();
 }
-