[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ke / amd64 / cpu.c
diff --git a/reactos/ntoskrnl/ke/amd64/cpu.c b/reactos/ntoskrnl/ke/amd64/cpu.c
new file mode 100644 (file)
index 0000000..90a6b2c
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            ntoskrnl/ke/amd64/cpu.c
+ * PURPOSE:         Routines for CPU-level support
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+ *                  Timo Kreuzer (timo.kreuzer@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+/* FIXME: Local EFLAGS defines not used anywhere else */
+#define EFLAGS_IOPL     0x3000
+#define EFLAGS_NF       0x4000
+#define EFLAGS_RF       0x10000
+#define EFLAGS_ID       0x200000
+
+/* GLOBALS *******************************************************************/
+
+/* The Boot TSS */
+KTSS64 KiBootTss;
+
+/* CPU Features and Flags */
+ULONG KeI386CpuType;
+ULONG KeI386CpuStep;
+ULONG KeProcessorArchitecture;
+ULONG KeProcessorLevel;
+ULONG KeProcessorRevision;
+ULONG KeFeatureBits;
+ULONG KeI386MachineType;
+ULONG KeI386NpxPresent = 1;
+ULONG KeLargestCacheLine = 0x40;
+ULONG KiDmaIoCoherency = 0;
+CHAR KeNumberProcessors = 0;
+KAFFINITY KeActiveProcessors = 1;
+BOOLEAN KiI386PentiumLockErrataPresent;
+BOOLEAN KiSMTProcessorsPresent;
+
+/* Freeze data */
+KIRQL KiOldIrql;
+ULONG KiFreezeFlag;
+
+/* Flush data */
+volatile LONG KiTbFlushTimeStamp;
+
+/* CPU Signatures */
+static const CHAR CmpIntelID[]       = "GenuineIntel";
+static const CHAR CmpAmdID[]         = "AuthenticAMD";
+static const CHAR CmpCyrixID[]       = "CyrixInstead";
+static const CHAR CmpTransmetaID[]   = "GenuineTMx86";
+static const CHAR CmpCentaurID[]     = "CentaurHauls";
+static const CHAR CmpRiseID[]        = "RiseRiseRise";
+
+/* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/
+
+VOID
+NTAPI
+CPUID(IN ULONG InfoType,
+      OUT PULONG CpuInfoEax,
+      OUT PULONG CpuInfoEbx,
+      OUT PULONG CpuInfoEcx,
+      OUT PULONG CpuInfoEdx)
+{
+    ULONG CpuInfo[4];
+
+    /* Perform the CPUID Operation */
+    __cpuid((int*)CpuInfo, InfoType);
+
+    /* Return the results */
+    *CpuInfoEax = CpuInfo[0];
+    *CpuInfoEbx = CpuInfo[1];
+    *CpuInfoEcx = CpuInfo[2];
+    *CpuInfoEdx = CpuInfo[3];
+}
+
+/* FUNCTIONS *****************************************************************/
+
+VOID
+NTAPI
+KiSetProcessorType(VOID)
+{
+    ULONG64 EFlags;
+    INT Reg[4];
+    ULONG Stepping, Type;
+
+    /* Start by assuming no CPUID data */
+    KeGetCurrentPrcb()->CpuID = 0;
+
+    /* Save EFlags */
+    EFlags = __readeflags();
+
+    /* Do CPUID 1 now */
+    __cpuid(Reg, 1);
+
+    /*
+     * Get the Stepping and Type. The stepping contains both the
+     * Model and the Step, while the Type contains the returned Type.
+     * We ignore the family.
+     *
+     * For the stepping, we convert this: zzzzzzxy into this: x0y
+     */
+    Stepping = Reg[0] & 0xF0;
+    Stepping <<= 4;
+    Stepping += (Reg[0] & 0xFF);
+    Stepping &= 0xF0F;
+    Type = Reg[0] & 0xF00;
+    Type >>= 8;
+
+    /* Save them in the PRCB */
+    KeGetCurrentPrcb()->CpuID = TRUE;
+    KeGetCurrentPrcb()->CpuType = (UCHAR)Type;
+    KeGetCurrentPrcb()->CpuStep = (USHORT)Stepping;
+
+    /* Restore EFLAGS */
+    __writeeflags(EFlags);
+}
+
+ULONG
+NTAPI
+KiGetCpuVendor(VOID)
+{
+    PKPRCB Prcb = KeGetCurrentPrcb();
+    INT Vendor[5];
+
+    /* Get the Vendor ID and null-terminate it */
+    __cpuid(Vendor, 0);
+
+    /* Copy it to the PRCB and null-terminate it */
+    *(ULONG*)&Prcb->VendorString[0] = Vendor[1]; // ebx
+    *(ULONG*)&Prcb->VendorString[4] = Vendor[3]; // edx
+    *(ULONG*)&Prcb->VendorString[8] = Vendor[2]; // ecx
+    *(ULONG*)&Prcb->VendorString[12] = 0;
+
+    /* Now check the CPU Type */
+    if (!strcmp((PCHAR)Prcb->VendorString, CmpIntelID))
+    {
+        return CPU_INTEL;
+    }
+    else if (!strcmp((PCHAR)Prcb->VendorString, CmpAmdID))
+    {
+        return CPU_AMD;
+    }
+    else if (!strcmp((PCHAR)Prcb->VendorString, CmpCyrixID))
+    {
+        DPRINT1("Cyrix CPUs not fully supported\n");
+        return 0;
+    }
+    else if (!strcmp((PCHAR)Prcb->VendorString, CmpTransmetaID))
+    {
+        DPRINT1("Transmeta CPUs not fully supported\n");
+        return 0;
+    }
+    else if (!strcmp((PCHAR)Prcb->VendorString, CmpCentaurID))
+    {
+        DPRINT1("VIA CPUs not fully supported\n");
+        return 0;
+    }
+    else if (!strcmp((PCHAR)Prcb->VendorString, CmpRiseID))
+    {
+        DPRINT1("Rise CPUs not fully supported\n");
+        return 0;
+    }
+
+    /* Invalid CPU */
+    return 0;
+}
+
+ULONG
+NTAPI
+KiGetFeatureBits(VOID)
+{
+    PKPRCB Prcb = KeGetCurrentPrcb();
+    ULONG Vendor;
+    ULONG FeatureBits = KF_WORKING_PTE;
+    INT Reg[4];
+    ULONG CpuFeatures = 0;
+
+    /* Get the Vendor ID */
+    Vendor = KiGetCpuVendor();
+
+    /* Make sure we got a valid vendor ID at least. */
+    if (!Vendor) return FeatureBits;
+
+    /* Get the CPUID Info. Features are in Reg[3]. */
+    __cpuid(Reg, 1);
+
+    /* Set the initial APIC ID */
+    Prcb->InitialApicId = (UCHAR)(Reg[1] >> 24);
+
+    /* Set the current features */
+    CpuFeatures = Reg[3];
+
+    /* Convert all CPUID Feature bits into our format */
+    if (CpuFeatures & 0x00000002) FeatureBits |= KF_V86_VIS | KF_CR4;
+    if (CpuFeatures & 0x00000008) FeatureBits |= KF_LARGE_PAGE | KF_CR4;
+    if (CpuFeatures & 0x00000010) FeatureBits |= KF_RDTSC;
+    if (CpuFeatures & 0x00000100) FeatureBits |= KF_CMPXCHG8B;
+    if (CpuFeatures & 0x00000800) FeatureBits |= KF_FAST_SYSCALL;
+    if (CpuFeatures & 0x00001000) FeatureBits |= KF_MTRR;
+    if (CpuFeatures & 0x00002000) FeatureBits |= KF_GLOBAL_PAGE | KF_CR4;
+    if (CpuFeatures & 0x00008000) FeatureBits |= KF_CMOV;
+    if (CpuFeatures & 0x00010000) FeatureBits |= KF_PAT;
+    if (CpuFeatures & 0x00200000) FeatureBits |= KF_DTS;
+    if (CpuFeatures & 0x00800000) FeatureBits |= KF_MMX;
+    if (CpuFeatures & 0x01000000) FeatureBits |= KF_FXSR;
+    if (CpuFeatures & 0x02000000) FeatureBits |= KF_XMMI;
+    if (CpuFeatures & 0x04000000) FeatureBits |= KF_XMMI64;
+
+#if 0
+    if (Reg[2] & 0x00000001) FeatureBits |= KF_SSE3NEW;
+    if (Reg[2] & 0x00000008) FeatureBits |= KF_MONITOR;
+    if (Reg[2] & 0x00000200) FeatureBits |= KF_SSE3SUP;
+    if (Reg[2] & 0x00002000) FeatureBits |= KF_CMPXCHG16B;
+    if (Reg[2] & 0x00080000) FeatureBits |= KF_SSE41;
+    if (Reg[2] & 0x00800000) FeatureBits |= KF_POPCNT;
+#endif
+
+    /* Check if the CPU has hyper-threading */
+    if (CpuFeatures & 0x10000000)
+    {
+        /* Set the number of logical CPUs */
+        Prcb->LogicalProcessorsPerPhysicalProcessor = (UCHAR)(Reg[1] >> 16);
+        if (Prcb->LogicalProcessorsPerPhysicalProcessor > 1)
+        {
+            /* We're on dual-core */
+            KiSMTProcessorsPresent = TRUE;
+        }
+    }
+    else
+    {
+        /* We only have a single CPU */
+        Prcb->LogicalProcessorsPerPhysicalProcessor = 1;
+    }
+
+    /* Check extended cpuid features */
+    __cpuid(Reg, 0x80000000);
+    if ((Reg[0] & 0xffffff00) == 0x80000000)
+    {
+        /* Check if CPUID 0x80000001 is supported */
+        if (Reg[0] >= 0x80000001)
+        {
+            /* Check which extended features are available. */
+            __cpuid(Reg, 0x80000001);
+
+            /* Check if NX-bit is supported */
+            if (Reg[3] & 0x00100000) FeatureBits |= KF_NX_BIT;
+
+            /* Now handle each features for each CPU Vendor */
+            switch (Vendor)
+            {
+                case CPU_AMD:
+                    if (Reg[3] & 0x80000000) FeatureBits |= KF_3DNOW;
+                    break;
+            }
+        }
+    }
+
+    /* Return the Feature Bits */
+    return FeatureBits;
+}
+
+VOID
+NTAPI
+KiGetCacheInformation(VOID)
+{
+    PKIPCR Pcr = (PKIPCR)KeGetPcr();
+    ULONG Vendor;
+    INT Data[4];
+    ULONG CacheRequests = 0, i;
+    ULONG CurrentRegister;
+    UCHAR RegisterByte;
+    BOOLEAN FirstPass = TRUE;
+
+    /* Set default L2 size */
+    Pcr->SecondLevelCacheSize = 0;
+
+    /* Get the Vendor ID and make sure we support CPUID */
+    Vendor = KiGetCpuVendor();
+    if (!Vendor) return;
+
+    /* Check the Vendor ID */
+    switch (Vendor)
+    {
+        /* Handle Intel case */
+        case CPU_INTEL:
+
+            /*Check if we support CPUID 2 */
+            __cpuid(Data, 0);
+            if (Data[0] >= 2)
+            {
+                /* We need to loop for the number of times CPUID will tell us to */
+                do
+                {
+                    /* Do the CPUID call */
+                    __cpuid(Data, 2);
+
+                    /* Check if it was the first call */
+                    if (FirstPass)
+                    {
+                        /*
+                         * The number of times to loop is the first byte. Read
+                         * it and then destroy it so we don't get confused.
+                         */
+                        CacheRequests = Data[0] & 0xFF;
+                        Data[0] &= 0xFFFFFF00;
+
+                        /* Don't go over this again */
+                        FirstPass = FALSE;
+                    }
+
+                    /* Loop all 4 registers */
+                    for (i = 0; i < 4; i++)
+                    {
+                        /* Get the current register */
+                        CurrentRegister = Data[i];
+
+                        /*
+                         * If the upper bit is set, then this register should
+                         * be skipped.
+                         */
+                        if (CurrentRegister & 0x80000000) continue;
+
+                        /* Keep looping for every byte inside this register */
+                        while (CurrentRegister)
+                        {
+                            /* Read a byte, skip a byte. */
+                            RegisterByte = (UCHAR)(CurrentRegister & 0xFF);
+                            CurrentRegister >>= 8;
+                            if (!RegisterByte) continue;
+
+                            /*
+                             * Valid values are from 0x40 (0 bytes) to 0x49
+                             * (32MB), or from 0x80 to 0x89 (same size but
+                             * 8-way associative.
+                             */
+                            if (((RegisterByte > 0x40) &&
+                                 (RegisterByte <= 0x49)) ||
+                                ((RegisterByte > 0x80) &&
+                                (RegisterByte <= 0x89)))
+                            {
+                                /* Mask out only the first nibble */
+                                RegisterByte &= 0x0F;
+
+                                /* Set the L2 Cache Size */
+                                Pcr->SecondLevelCacheSize = 0x10000 <<
+                                                            RegisterByte;
+                            }
+                        }
+                    }
+                } while (--CacheRequests);
+            }
+            break;
+
+        case CPU_AMD:
+
+            /* Check if we support CPUID 0x80000006 */
+            __cpuid(Data, 0x80000000);
+            if (Data[0] >= 6)
+            {
+                /* Get 2nd level cache and tlb size */
+                __cpuid(Data, 0x80000006);
+
+                /* Set the L2 Cache Size */
+                Pcr->SecondLevelCacheSize = (Data[2] & 0xFFFF0000) >> 6;
+            }
+            break;
+    }
+}
+
+VOID
+FASTCALL
+KiInitializeTss(IN PKTSS64 Tss,
+                IN UINT64 Stack)
+{
+    PKGDTENTRY64 TssEntry;
+
+    /* Get pointer to the GDT entry */
+    TssEntry = KiGetGdtEntry(KeGetPcr()->GdtBase, KGDT64_SYS_TSS);
+
+    /* Initialize the GDT entry */
+    KiInitGdtEntry(TssEntry, (ULONG64)Tss, sizeof(KTSS64), AMD64_TSS, 0);
+
+    /* Zero out the TSS */
+    RtlZeroMemory(Tss, sizeof(KTSS64));
+
+    /* FIXME: I/O Map? */
+    Tss->IoMapBase = 0x68;
+
+    /* Setup ring 0 stack pointer */
+    Tss->Rsp0 = Stack;
+
+    /* Setup a stack for Double Fault Traps */
+    Tss->Ist[1] = (ULONG64)KiDoubleFaultStack;
+
+    /* Setup a stack for CheckAbort Traps */
+    Tss->Ist[2] = (ULONG64)KiDoubleFaultStack;
+
+    /* Setup a stack for NMI Traps */
+    Tss->Ist[3] = (ULONG64)KiDoubleFaultStack;
+
+    /* Load the task register */
+    __ltr(KGDT64_SYS_TSS);
+}
+
+VOID
+NTAPI
+KeFlushCurrentTb(VOID)
+{
+    /* Flush the TLB by resetting CR3 */
+    __writecr3(__readcr3());
+}
+
+VOID
+NTAPI
+KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState)
+{
+    /* Restore the CR registers */
+    __writecr0(ProcessorState->SpecialRegisters.Cr0);
+//    __writecr2(ProcessorState->SpecialRegisters.Cr2);
+    __writecr3(ProcessorState->SpecialRegisters.Cr3);
+    __writecr4(ProcessorState->SpecialRegisters.Cr4);
+    __writecr8(ProcessorState->SpecialRegisters.Cr8);
+
+    /* Restore the DR registers */
+    __writedr(0, ProcessorState->SpecialRegisters.KernelDr0);
+    __writedr(1, ProcessorState->SpecialRegisters.KernelDr1);
+    __writedr(2, ProcessorState->SpecialRegisters.KernelDr2);
+    __writedr(3, ProcessorState->SpecialRegisters.KernelDr3);
+    __writedr(6, ProcessorState->SpecialRegisters.KernelDr6);
+    __writedr(7, ProcessorState->SpecialRegisters.KernelDr7);
+
+    /* Restore GDT, IDT, LDT and TSS */
+    __lgdt(&ProcessorState->SpecialRegisters.Gdtr.Limit);
+//    __lldt(&ProcessorState->SpecialRegisters.Ldtr);
+//    __ltr(&ProcessorState->SpecialRegisters.Tr);
+    __lidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
+
+//    __ldmxcsr(&ProcessorState->SpecialRegisters.MxCsr); // FIXME
+//    ProcessorState->SpecialRegisters.DebugControl
+//    ProcessorState->SpecialRegisters.LastBranchToRip
+//    ProcessorState->SpecialRegisters.LastBranchFromRip
+//    ProcessorState->SpecialRegisters.LastExceptionToRip
+//    ProcessorState->SpecialRegisters.LastExceptionFromRip
+
+    /* Restore MSRs */
+    __writemsr(X86_MSR_GSBASE, ProcessorState->SpecialRegisters.MsrGsBase);
+    __writemsr(X86_MSR_KERNEL_GSBASE, ProcessorState->SpecialRegisters.MsrGsSwap);
+    __writemsr(X86_MSR_STAR, ProcessorState->SpecialRegisters.MsrStar);
+    __writemsr(X86_MSR_LSTAR, ProcessorState->SpecialRegisters.MsrLStar);
+    __writemsr(X86_MSR_CSTAR, ProcessorState->SpecialRegisters.MsrCStar);
+    __writemsr(X86_MSR_SFMASK, ProcessorState->SpecialRegisters.MsrSyscallMask);
+
+}
+
+VOID
+NTAPI
+KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState)
+{
+    /* Save the CR registers */
+    ProcessorState->SpecialRegisters.Cr0 = __readcr0();
+    ProcessorState->SpecialRegisters.Cr2 = __readcr2();
+    ProcessorState->SpecialRegisters.Cr3 = __readcr3();
+    ProcessorState->SpecialRegisters.Cr4 = __readcr4();
+    ProcessorState->SpecialRegisters.Cr8 = __readcr8();
+
+    /* Save the DR registers */
+    ProcessorState->SpecialRegisters.KernelDr0 = __readdr(0);
+    ProcessorState->SpecialRegisters.KernelDr1 = __readdr(1);
+    ProcessorState->SpecialRegisters.KernelDr2 = __readdr(2);
+    ProcessorState->SpecialRegisters.KernelDr3 = __readdr(3);
+    ProcessorState->SpecialRegisters.KernelDr6 = __readdr(6);
+    ProcessorState->SpecialRegisters.KernelDr7 = __readdr(7);
+
+    /* Save GDT, IDT, LDT and TSS */
+    __sgdt(&ProcessorState->SpecialRegisters.Gdtr.Limit);
+    __sldt(&ProcessorState->SpecialRegisters.Ldtr);
+    __str(&ProcessorState->SpecialRegisters.Tr);
+    __sidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
+
+//    __stmxcsr(&ProcessorState->SpecialRegisters.MxCsr);
+//    ProcessorState->SpecialRegisters.DebugControl = 
+//    ProcessorState->SpecialRegisters.LastBranchToRip = 
+//    ProcessorState->SpecialRegisters.LastBranchFromRip = 
+//    ProcessorState->SpecialRegisters.LastExceptionToRip = 
+//    ProcessorState->SpecialRegisters.LastExceptionFromRip = 
+
+    /* Save MSRs */
+    ProcessorState->SpecialRegisters.MsrGsBase = __readmsr(X86_MSR_GSBASE);
+    ProcessorState->SpecialRegisters.MsrGsSwap = __readmsr(X86_MSR_KERNEL_GSBASE);
+    ProcessorState->SpecialRegisters.MsrStar = __readmsr(X86_MSR_STAR);
+    ProcessorState->SpecialRegisters.MsrLStar = __readmsr(X86_MSR_LSTAR);
+    ProcessorState->SpecialRegisters.MsrCStar = __readmsr(X86_MSR_CSTAR);
+    ProcessorState->SpecialRegisters.MsrSyscallMask = __readmsr(X86_MSR_SFMASK);
+}
+
+VOID
+NTAPI
+KeFlushEntireTb(IN BOOLEAN Invalid,
+                IN BOOLEAN AllProcessors)
+{
+    KIRQL OldIrql;
+
+    // FIXME: halfplemented
+    /* Raise the IRQL for the TB Flush */
+    OldIrql = KeRaiseIrqlToSynchLevel();
+
+    /* Flush the TB for the Current CPU, and update the flush stamp */
+    KeFlushCurrentTb();
+
+    /* Update the flush stamp and return to original IRQL */
+    InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1);
+    KeLowerIrql(OldIrql);
+
+}
+
+KAFFINITY
+NTAPI
+KeQueryActiveProcessors(VOID)
+{
+    PAGED_CODE();
+
+    /* Simply return the number of active processors */
+    return KeActiveProcessors;
+}
+
+NTSTATUS
+NTAPI
+KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)
+{
+    UNIMPLEMENTED;
+    return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS
+NTAPI
+KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save)
+{
+    UNIMPLEMENTED;
+    return STATUS_UNSUCCESSFUL;
+}
+
+BOOLEAN
+NTAPI
+KeInvalidateAllCaches(VOID)
+{
+    /* Invalidate all caches */
+    __wbinvd();
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+ULONG
+NTAPI
+KeGetRecommendedSharedDataAlignment(VOID)
+{
+    /* Return the global variable */
+    return KeLargestCacheLine;
+}
+
+/*
+ * @implemented
+ */
+VOID
+__cdecl
+KeSaveStateForHibernate(IN PKPROCESSOR_STATE State)
+{
+    /* Capture the context */
+    RtlCaptureContext(&State->ContextFrame);
+
+    /* Capture the control state */
+    KiSaveProcessorControlState(State);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+KeSetDmaIoCoherency(IN ULONG Coherency)
+{
+    /* Save the coherency globally */
+    KiDmaIoCoherency = Coherency;
+}