- KdDebuggerNotPresent should be FALSE by default.
[reactos.git] / reactos / ntoskrnl / ke / i386 / cpu.c
index cf14ca9..b07ed5f 100644 (file)
-/*\r
- * PROJECT:         ReactOS Kernel\r
- * LICENSE:         GPL - See COPYING in the top level directory\r
- * FILE:            ntoskrnl/ke/i386/cpu.c\r
- * PURPOSE:         Routines for CPU-level support\r
- * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)\r
- */\r
-\r
-/* INCLUDES *****************************************************************/\r
-\r
-#include <ntoskrnl.h>\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-/* FIXME: Local EFLAGS defines not used anywhere else */\r
-#define EFLAGS_IOPL     0x3000\r
-#define EFLAGS_NF       0x4000\r
-#define EFLAGS_RF       0x10000\r
-#define EFLAGS_ID       0x200000\r
-\r
-/* GLOBALS *******************************************************************/\r
-\r
-/* The Boot TSS */\r
-KTSS KiBootTss;\r
-\r
-/* The TSS to use for Double Fault Traps (INT 0x9) */\r
-UCHAR KiDoubleFaultTSS[KTSS_IO_MAPS];\r
-\r
-/* The Boot GDT (FIXME: should have more entries */\r
-KGDTENTRY KiBootGdt[12] =\r
-{\r
-    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_NULL */\r
-    {0xffff, 0x0000, {{0x00, 0x9a, 0xcf, 0x00}}},       /* KGDT_R0_CODE */\r
-    {0xffff, 0x0000, {{0x00, 0x92, 0xcf, 0x00}}},       /* KGDT_R0_DATA */\r
-    {0xffff, 0x0000, {{0x00, 0xfa, 0xcf, 0x00}}},       /* KGDT_R3_CODE */\r
-    {0xffff, 0x0000, {{0x00, 0xf2, 0xcf, 0x00}}},       /* KGDT_R3_DATA*/\r
-    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_TSS */\r
-    {0x0fff, 0x0000, {{0x00, 0x92, 0x00, 0xff}}},       /* KGDT_R0_PCR */\r
-    {0x0fff, 0x0000, {{0x00, 0xf2, 0x00, 0x00}}},       /* KGDT_R3_TEB */\r
-    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_UNUSED */\r
-    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_LDT */\r
-    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_DF_TSS */\r
-    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}}        /* KGDT_NMI_TSS */\r
-};\r
-\r
-/* GDT Descriptor */\r
-KDESCRIPTOR KiGdtDescriptor = {sizeof(KiBootGdt), (ULONG)KiBootGdt};\r
-\r
-/* CPU Features and Flags */\r
-ULONG KeI386CpuType;\r
-ULONG KeI386CpuStep;\r
-ULONG KeProcessorArchitecture;\r
-ULONG KeProcessorLevel;\r
-ULONG KeProcessorRevision;\r
-ULONG KeFeatureBits;\r
-ULONG KiFastSystemCallDisable = 1;\r
-ULONG KeI386NpxPresent = 0;\r
-ULONG MxcsrFeatureMask = 0;\r
-ULONG KeI386XMMIPresent = 0;\r
-ULONG KeI386FxsrPresent = 0;\r
-ULONG Ke386Pae = FALSE;\r
-ULONG Ke386GlobalPagesEnabled = FALSE;\r
-ULONG Ke386NoExecute = FALSE;\r
-BOOLEAN KiI386PentiumLockErrataPresent;\r
-\r
-/* CPU Signatures */\r
-CHAR CmpIntelID[]       = "GenuineIntel";\r
-CHAR CmpAmdID[]         = "AuthenticAMD";\r
-CHAR CmpCyrixID[]       = "CyrixInstead";\r
-CHAR CmpTransmetaID[]   = "GenuineTMx86";\r
-CHAR CmpCentaurID[]     = "CentaurHauls";\r
-CHAR CmpRiseID[]        = "RiseRiseRise";\r
-\r
-/* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/\r
-\r
-VOID\r
-NTAPI\r
-CPUID(IN ULONG CpuInfo[4],\r
-      IN ULONG InfoType)\r
-{\r
-    Ki386Cpuid(InfoType, &CpuInfo[0], &CpuInfo[1], &CpuInfo[2], &CpuInfo[3]);\r
-}\r
-\r
-VOID\r
-WRMSR(IN ULONG Register,\r
-      IN LONGLONG Value)\r
-{\r
-    LARGE_INTEGER LargeVal;\r
-    LargeVal.QuadPart = Value;\r
-    Ke386Wrmsr(Register, LargeVal.HighPart, LargeVal.LowPart);\r
-}\r
-\r
-LONGLONG\r
-RDMSR(IN ULONG Register)\r
-{\r
-    LARGE_INTEGER LargeVal;\r
-    Ke386Rdmsr(Register, LargeVal.HighPart, LargeVal.LowPart);\r
-    return LargeVal.QuadPart;\r
-}\r
-\r
-/* FUNCTIONS *****************************************************************/\r
-\r
-VOID\r
-NTAPI\r
-KiSetProcessorType(VOID)\r
-{\r
-    ULONG EFlags, NewEFlags;\r
-    ULONG Reg[4];\r
-    ULONG Stepping, Type;\r
-\r
-    /* Start by assuming no CPUID data */\r
-    KeGetCurrentPrcb()->CpuID = 0;\r
-\r
-    /* Save EFlags */\r
-    Ke386SaveFlags(EFlags);\r
-\r
-    /* XOR out the ID bit and update EFlags */\r
-    NewEFlags = EFlags ^ EFLAGS_ID;\r
-    Ke386RestoreFlags(NewEFlags);\r
-\r
-    /* Get them back and see if they were modified */\r
-    Ke386SaveFlags(NewEFlags);\r
-    if (NewEFlags != EFlags)\r
-    {\r
-        /* The modification worked, so CPUID exists. Set the ID Bit again. */\r
-        EFlags |= EFLAGS_ID;\r
-        Ke386RestoreFlags(EFlags);\r
-\r
-        /* Peform CPUID 0 to see if CPUID 1 is supported */\r
-        CPUID(Reg, 0);\r
-        if (Reg[0] > 0)\r
-        {\r
-            /* Do CPUID 1 now */\r
-            CPUID(Reg, 1);\r
-\r
-            /*\r
-             * Get the Stepping and Type. The stepping contains both the\r
-             * Model and the Step, while the Type contains the returned Type.\r
-             * We ignore the family.\r
-             *\r
-             * For the stepping, we convert this: zzzzzzxy into this: x0y\r
-             */\r
-            Stepping = Reg[0] & 0xF0;\r
-            Stepping <<= 4;\r
-            Stepping += (Reg[0] & 0xFF);\r
-            Stepping &= 0xF0F;\r
-            Type = Reg[0] & 0xF00;\r
-            Type >>= 8;\r
-\r
-            /* Save them in the PRCB */\r
-            KeGetCurrentPrcb()->CpuID = TRUE;\r
-            KeGetCurrentPrcb()->CpuType = (UCHAR)Type;\r
-            KeGetCurrentPrcb()->CpuStep = (USHORT)Stepping;\r
-        }\r
-        else\r
-        {\r
-            DPRINT1("CPUID Support lacking\n");\r
-        }\r
-    }\r
-    else\r
-    {\r
-        DPRINT1("CPUID Support lacking\n");\r
-    }\r
-\r
-    /* Restore EFLAGS */\r
-    Ke386RestoreFlags(EFlags);\r
-}\r
-\r
-ULONG\r
-NTAPI\r
-KiGetCpuVendor(VOID)\r
-{\r
-    PKPRCB Prcb = KeGetCurrentPrcb();\r
-    ULONG Vendor[5];\r
-\r
-    /* Assume no Vendor ID and fail if no CPUID Support. */\r
-    Prcb->VendorString[0] = 0;\r
-    if (!Prcb->CpuID) return 0;\r
-\r
-    /* Get the Vendor ID and null-terminate it */\r
-    CPUID(Vendor, 0);\r
-    Vendor[4] = 0;\r
-\r
-    /* Re-arrange vendor string */\r
-    Vendor[5] = Vendor[2];\r
-    Vendor[2] = Vendor[3];\r
-    Vendor[3] = Vendor[5];\r
-\r
-    /* Copy it to the PRCB and null-terminate it again */\r
-    RtlCopyMemory(Prcb->VendorString,\r
-                  &Vendor[1],\r
-                  sizeof(Prcb->VendorString) - sizeof(CHAR));\r
-    Prcb->VendorString[sizeof(Prcb->VendorString) - sizeof(CHAR)] = ANSI_NULL;\r
-\r
-    /* Now check the CPU Type */\r
-    if (!strcmp(Prcb->VendorString, CmpIntelID))\r
-    {\r
-        return CPU_INTEL;\r
-    }\r
-    else if (!strcmp(Prcb->VendorString, CmpAmdID))\r
-    {\r
-        return CPU_AMD;\r
-    }\r
-    else if (!strcmp(Prcb->VendorString, CmpCyrixID))\r
-    {\r
-        DPRINT1("Cyrix CPUs not fully supported\n");\r
-        return 0;\r
-    }\r
-    else if (!strcmp(Prcb->VendorString, CmpTransmetaID))\r
-    {\r
-        DPRINT1("Transmeta CPUs not fully supported\n");\r
-        return 0;\r
-    }\r
-    else if (!strcmp(Prcb->VendorString, CmpCentaurID))\r
-    {\r
-        DPRINT1("VIA CPUs not fully supported\n");\r
-        return 0;\r
-    }\r
-    else if (!strcmp(Prcb->VendorString, CmpRiseID))\r
-    {\r
-        DPRINT1("Rise CPUs not fully supported\n");\r
-        return 0;\r
-    }\r
-\r
-    /* Invalid CPU */\r
-    return 0;\r
-}\r
-\r
-ULONG\r
-NTAPI\r
-KiGetFeatureBits(VOID)\r
-{\r
-    PKPRCB Prcb = KeGetCurrentPrcb();\r
-    ULONG Vendor;\r
-    ULONG FeatureBits = KF_WORKING_PTE;\r
-    ULONG Reg[4];\r
-    BOOLEAN ExtendedCPUID = TRUE;\r
-    ULONG CpuFeatures = 0;\r
-\r
-    /* Get the Vendor ID */\r
-    Vendor = KiGetCpuVendor();\r
-\r
-    /* Make sure we got a valid vendor ID at least. */\r
-    if (!Vendor) return FeatureBits;\r
-\r
-    /* Get the CPUID Info. Features are in Reg[3]. */\r
-    CPUID(Reg, 1);\r
-\r
-    /* Check for AMD CPU */\r
-    if (Vendor == CPU_AMD)\r
-    {\r
-        /* Check if this is a K5 or higher. */\r
-        if ((Reg[0] & 0x0F00) >= 0x0500)\r
-        {\r
-            /* Check if this is a K5 specifically. */\r
-            if ((Reg[0] & 0x0F00) == 0x0500)\r
-            {\r
-                /* Get the Model Number */\r
-                switch (Reg[0] & 0x00F0)\r
-                {\r
-                    /* Check if this is the Model 1 */\r
-                    case 0x0010:\r
-\r
-                        /* Check if this is Step 0 or 1. They don't support PGE */\r
-                        if ((Reg[0] & 0x000F) > 0x03) break;\r
-\r
-                    case 0x0000:\r
-\r
-                        /* Model 0 doesn't support PGE at all. */\r
-                        Reg[3] &= ~0x2000;\r
-                        break;\r
-\r
-                    case 0x0080:\r
-\r
-                        /* K6-2, Step 8 and over have support for MTRR. */\r
-                        if ((Reg[0] & 0x000F) >= 0x8) FeatureBits |= KF_AMDK6MTRR;\r
-                        break;\r
-\r
-                    case 0x0090:\r
-\r
-                        /* As does the K6-3 */\r
-                        FeatureBits |= KF_AMDK6MTRR;\r
-                        break;\r
-\r
-                    default:\r
-                        break;\r
-                }\r
-            }\r
-        }\r
-        else\r
-        {\r
-            /* Familes below 5 don't support PGE, PSE or CMOV at all */\r
-            Reg[3] &= ~(0x08 | 0x2000 | 0x8000);\r
-\r
-            /* They also don't support advanced CPUID functions. */\r
-            ExtendedCPUID = FALSE;\r
-        }\r
-\r
-        /* Set the current features */\r
-        CpuFeatures = Reg[3];\r
-    }\r
-\r
-    /* Now check if this is Intel */\r
-    if (Vendor == CPU_INTEL)\r
-    {\r
-        /* Check if it's a P6 */\r
-        if (Prcb->CpuType == 6)\r
-        {\r
-            /* Perform the special sequence to get the MicroCode Signature */\r
-            WRMSR(0x8B, 0);\r
-            CPUID(Reg, 1);\r
-            Prcb->UpdateSignature.QuadPart = RDMSR(0x8B);\r
-        }\r
-        else if (Prcb->CpuType == 5)\r
-        {\r
-            /* On P5, enable workaround for the LOCK errata. */\r
-            KiI386PentiumLockErrataPresent = TRUE;\r
-        }\r
-\r
-        /* Check for broken P6 with bad SMP PTE implementation */\r
-        if (((Reg[0] & 0x0FF0) == 0x0610 && (Reg[0] & 0x000F) <= 0x9) ||\r
-            ((Reg[0] & 0x0FF0) == 0x0630 && (Reg[0] & 0x000F) <= 0x4))\r
-        {\r
-            /* Remove support for correct PTE support. */\r
-            FeatureBits &= ~KF_WORKING_PTE;\r
-        }\r
-\r
-        /* Set the current features */\r
-        CpuFeatures = Reg[3];\r
-    }\r
-\r
-    /* Convert all CPUID Feature bits into our format */\r
-    if (CpuFeatures & 0x00000002) FeatureBits |= KF_V86_VIS | KF_CR4;\r
-    if (CpuFeatures & 0x00000008) FeatureBits |= KF_LARGE_PAGE | KF_CR4;\r
-    if (CpuFeatures & 0x00000010) FeatureBits |= KF_RDTSC;\r
-    if (CpuFeatures & 0x00000100) FeatureBits |= KF_CMPXCHG8B;\r
-    if (CpuFeatures & 0x00000800) FeatureBits |= KF_FAST_SYSCALL;\r
-    if (CpuFeatures & 0x00001000) FeatureBits |= KF_MTRR;\r
-    if (CpuFeatures & 0x00002000) FeatureBits |= KF_GLOBAL_PAGE | KF_CR4;\r
-    if (CpuFeatures & 0x00008000) FeatureBits |= KF_CMOV;\r
-    if (CpuFeatures & 0x00010000) FeatureBits |= KF_PAT;\r
-    if (CpuFeatures & 0x00800000) FeatureBits |= KF_MMX;\r
-    if (CpuFeatures & 0x01000000) FeatureBits |= KF_FXSR;\r
-    if (CpuFeatures & 0x02000000) FeatureBits |= KF_XMMI;\r
-\r
-    /* Check if CPUID 0x80000000 is supported */\r
-    if (ExtendedCPUID)\r
-    {\r
-        /* Do the call */\r
-        CPUID(Reg, 0x80000000);\r
-        if ((Reg[0] & 0xffffff00) == 0x80000000)\r
-        {\r
-            /* Check if CPUID 0x80000001 is supported */\r
-            if (Reg[0] >= 0x80000001)\r
-            {\r
-                /* Check which extended features are available. */\r
-                CPUID(Reg, 0x80000001);\r
-\r
-                /* Now handle each features for each CPU Vendor */\r
-                switch (Vendor)\r
-                {\r
-                    case CPU_AMD:\r
-                        if (Reg[3] & 0x80000000) FeatureBits |= KF_3DNOW;\r
-                        break;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    /* Return the Feature Bits */\r
-    return FeatureBits;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KiGetCacheInformation(VOID)\r
-{\r
-    PKIPCR Pcr = (PKIPCR)KeGetPcr();\r
-    ULONG Vendor;\r
-    ULONG Data[4];\r
-    ULONG CacheRequests = 0, i;\r
-    ULONG CurrentRegister;\r
-    UCHAR RegisterByte;\r
-    BOOLEAN FirstPass = TRUE;\r
-\r
-    /* Set default L2 size */\r
-    Pcr->SecondLevelCacheSize = 0;\r
-\r
-    /* Get the Vendor ID and make sure we support CPUID */\r
-    Vendor = KiGetCpuVendor();\r
-    if (!Vendor) return;\r
-\r
-    /* Check the Vendor ID */\r
-    switch (Vendor)\r
-    {\r
-        /* Handle Intel case */\r
-        case CPU_INTEL:\r
-\r
-            /*Check if we support CPUID 2 */\r
-            CPUID(Data, 0);\r
-            if (Data[0] >= 2)\r
-            {\r
-                /* We need to loop for the number of times CPUID will tell us to */\r
-                do\r
-                {\r
-                    /* Do the CPUID call */\r
-                    CPUID(Data, 2);\r
-\r
-                    /* Check if it was the first call */\r
-                    if (FirstPass)\r
-                    {\r
-                        /*\r
-                         * The number of times to loop is the first byte. Read\r
-                         * it and then destroy it so we don't get confused.\r
-                         */\r
-                        CacheRequests = Data[0] & 0xFF;\r
-                        Data[0] &= 0xFFFFFF00;\r
-\r
-                        /* Don't go over this again */\r
-                        FirstPass = FALSE;\r
-                    }\r
-\r
-                    /* Loop all 4 registers */\r
-                    for (i = 0; i < 4; i++)\r
-                    {\r
-                        /* Get the current register */\r
-                        CurrentRegister = Data[i];\r
-\r
-                        /*\r
-                         * If the upper bit is set, then this register should\r
-                         * be skipped.\r
-                         */\r
-                        if (CurrentRegister & 0x80000000) continue;\r
-\r
-                        /* Keep looping for every byte inside this register */\r
-                        while (CurrentRegister)\r
-                        {\r
-                            /* Read a byte, skip a byte. */\r
-                            RegisterByte = (UCHAR)(CurrentRegister & 0xFF);\r
-                            CurrentRegister >>= 8;\r
-                            if (!RegisterByte) continue;\r
-\r
-                            /*\r
-                             * Valid values are from 0x40 (0 bytes) to 0x49\r
-                             * (32MB), or from 0x80 to 0x89 (same size but\r
-                             * 8-way associative.\r
-                             */\r
-                            if (((RegisterByte > 0x40) &&\r
-                                 (RegisterByte <= 0x49)) ||\r
-                                ((RegisterByte > 0x80) &&\r
-                                (RegisterByte <= 0x89)))\r
-                            {\r
-                                /* Mask out only the first nibble */\r
-                                RegisterByte &= 0x0F;\r
-\r
-                                /* Set the L2 Cache Size */\r
-                                Pcr->SecondLevelCacheSize = 0x10000 <<\r
-                                                            RegisterByte;\r
-                            }\r
-                        }\r
-                    }\r
-                } while (--CacheRequests);\r
-            }\r
-        break;\r
-\r
-    case CPU_AMD:\r
-\r
-        /* FIXME */\r
-        DPRINT1("Not handling AMD caches yet\n");\r
-        break;\r
-    }\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KiSetCR0Bits(VOID)\r
-{\r
-    ULONG Cr0;\r
-\r
-    /* Save current CR0 */\r
-    Cr0 = Ke386GetCr0();\r
-\r
-    /* If this is a 486, enable Write-Protection */\r
-    if (KeGetCurrentPrcb()->CpuType > 3) Cr0 |= CR0_WP;\r
-\r
-    /* Set new Cr0 */\r
-    Ke386SetCr0(Cr0);\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KiInitializeTSS2(IN PKTSS Tss,\r
-                 IN PKGDTENTRY TssEntry OPTIONAL)\r
-{\r
-    PUCHAR p;\r
-\r
-    /* Make sure the GDT Entry is valid */\r
-    if (TssEntry)\r
-    {\r
-        /* Set the Limit */\r
-        TssEntry->LimitLow = sizeof(KTSS) - 1;\r
-        TssEntry->HighWord.Bits.LimitHi &= 0xF0;\r
-    }\r
-\r
-    /* Now clear the I/O Map */\r
-    RtlFillMemory(Tss->IoMaps[0].IoMap, 8096, -1);\r
-\r
-    /* Initialize Interrupt Direction Maps */\r
-    p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);\r
-    RtlZeroMemory(p, 32);\r
-\r
-    /* Add DPMI support for interrupts */\r
-    p[0] = 4;\r
-    p[3] = 0x18;\r
-    p[4] = 0x18;\r
-\r
-    /* Initialize the default Interrupt Direction Map */\r
-    p = Tss->IntDirectionMap;\r
-    RtlZeroMemory(Tss->IntDirectionMap, 32);\r
-\r
-    /* Add DPMI support */\r
-    p[0] = 4;\r
-    p[3] = 0x18;\r
-    p[4] = 0x18;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KiInitializeTSS(IN PKTSS Tss)\r
-{\r
-    /* Set an invalid map base */\r
-    Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);\r
-\r
-    /* Disable traps during Task Switches */\r
-    Tss->Flags = 0;\r
-\r
-    /* Set LDT and Ring 0 SS */\r
-    Tss->LDT = 0;\r
-    Tss->Ss0 = KGDT_R0_DATA;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-Ki386InitializeTss(VOID)\r
-{\r
-    PKTSS Tss;\r
-    PKGDTENTRY TssEntry;\r
-    PKIDTENTRY TaskGateEntry;\r
-    PKIDT_ACCESS TaskGateAccess;\r
-\r
-    /* Initialize the boot TSS. */\r
-    Tss = &KiBootTss;\r
-    TssEntry = &KiBootGdt[KGDT_TSS / sizeof(KGDTENTRY)];\r
-    KiInitializeTSS2(Tss, TssEntry);\r
-    KiInitializeTSS(Tss);\r
-\r
-    /* Initialize a descriptor for the TSS */\r
-    TssEntry->HighWord.Bits.Type = I386_TSS;\r
-    TssEntry->HighWord.Bits.Pres = 1;\r
-    TssEntry->HighWord.Bits.Dpl = 0;\r
-    TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);\r
-    TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);\r
-    TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);\r
-\r
-    /* Load the task register */\r
-    __asm__("ltr %%ax":: "a" (KGDT_TSS));\r
-\r
-    /* Setup the Task Gate for Double Fault Traps */\r
-    TaskGateEntry = &KiIdt[8];\r
-    TaskGateAccess = (PKIDT_ACCESS)&TaskGateEntry->Access;\r
-#if 0\r
-    TaskGateAccess->SegmentType = I386_TASK_GATE;\r
-    TaskGateAccess->Present = 1;\r
-    TaskGateAccess->Dpl = 0;\r
-    TaskGateEntry->Selector = KGDT_DF_TSS;\r
-#endif\r
-\r
-    /* Initialize the TSS used for handling double faults. */\r
-    Tss = (PKTSS)KiDoubleFaultTSS;\r
-    KiInitializeTSS(Tss);\r
-    Tss->CR3 = _Ke386GetCr(3);\r
-    Tss->Esp0 = trap_stack_top;\r
-    Tss->Eip = PtrToUlong(KiTrap8);\r
-    Tss->Cs = KGDT_R0_CODE;\r
-    Tss->Fs = KGDT_R0_PCR;\r
-    Tss->Ss = Ke386GetSs();\r
-    Tss->Es = KGDT_R3_DATA | RPL_MASK;\r
-    Tss->Ds = KGDT_R3_DATA | RPL_MASK;\r
-\r
-    /* Setup the Double Trap TSS entry in the GDT */\r
-    TssEntry = &KiBootGdt[KGDT_DF_TSS / sizeof(KGDTENTRY)];\r
-    TssEntry->HighWord.Bits.Type = I386_TSS;\r
-    TssEntry->HighWord.Bits.Pres = 1;\r
-    TssEntry->HighWord.Bits.Dpl = 0;\r
-    TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);\r
-    TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);\r
-    TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);\r
-    TssEntry->LimitLow = KTSS_IO_MAPS;\r
-}\r
-\r
-/* This is a rather naive implementation of Ke(Save/Restore)FloatingPointState\r
-   which will not work for WDM drivers. Please feel free to improve */\r
-NTSTATUS\r
-NTAPI\r
-KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)\r
-{\r
-    PFNSAVE_FORMAT FpState;\r
-    ASSERT_IRQL(DISPATCH_LEVEL);\r
-\r
-    /* check if we are doing software emulation */\r
-    if (!KeI386NpxPresent) return STATUS_ILLEGAL_FLOAT_CONTEXT;\r
-\r
-    FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT));\r
-    if (!FpState) return STATUS_INSUFFICIENT_RESOURCES;\r
-\r
-    *((PVOID *) Save) = FpState;\r
-    asm volatile("fnsave %0\n\t" : "=m" (*FpState));\r
-\r
-    KeGetCurrentThread()->DispatcherHeader.NpxIrql = KeGetCurrentIrql();\r
-    return STATUS_SUCCESS;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save)\r
-{\r
-    PFNSAVE_FORMAT FpState = *((PVOID *) Save);\r
-\r
-    ASSERT(KeGetCurrentThread()->DispatcherHeader.NpxIrql == KeGetCurrentIrql());\r
-\r
-    asm volatile("fnclex\n\t");\r
-    asm volatile("frstor %0\n\t" : "=m" (*FpState));\r
-\r
-    ExFreePool(FpState);\r
-    return STATUS_SUCCESS;\r
-}\r
-\r
-VOID\r
-INIT_FUNCTION\r
-Ki386SetProcessorFeatures(VOID)\r
-{\r
-   OBJECT_ATTRIBUTES ObjectAttributes;\r
-   UNICODE_STRING KeyName =\r
-   RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Kernel");\r
-   UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"FastSystemCallDisable");\r
-   HANDLE KeyHandle;\r
-   ULONG ResultLength;\r
-   struct\r
-   {\r
-       KEY_VALUE_PARTIAL_INFORMATION Info;\r
-       UCHAR Buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,\r
-                                 Data[0]) + sizeof(ULONG)];\r
-   } ValueData;\r
-   NTSTATUS Status;\r
-   ULONG FastSystemCallDisable = 0;\r
-   \r
-   SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;\r
-   SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = FALSE;\r
-   SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =\r
-       (KeFeatureBits & KF_CMPXCHG8B) ? TRUE : FALSE;\r
-   SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =\r
-       (KeFeatureBits & KF_MMX) ? TRUE : FALSE;\r
-   SharedUserData->ProcessorFeatures[PF_PPC_MOVEMEM_64BIT_OK] = FALSE;\r
-   SharedUserData->ProcessorFeatures[PF_ALPHA_BYTE_INSTRUCTIONS] = FALSE;\r
-   SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =\r
-       (KeFeatureBits & KF_XMMI) ? TRUE : FALSE;\r
-   SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =\r
-       (KeFeatureBits & KF_RDTSC) ? TRUE : FALSE;\r
-\r
-   /* Does the CPU Support Fast System Call? */   \r
-   if (KeFeatureBits & KF_FAST_SYSCALL) {\r
-\r
-        /* FIXME: Check for Family == 6, Model < 3 and Stepping < 3 and disable */\r
-\r
-        /* Make sure it's not disabled in registry */\r
-        InitializeObjectAttributes(&ObjectAttributes,\r
-                                   &KeyName,\r
-                                   OBJ_CASE_INSENSITIVE,\r
-                                   NULL,\r
-                                   NULL);\r
-        Status = ZwOpenKey(&KeyHandle,\r
-                           KEY_QUERY_VALUE,\r
-                           &ObjectAttributes);\r
-\r
-        if (NT_SUCCESS(Status)) {\r
-\r
-            /* Read the Value then Close the Key */\r
-            Status = ZwQueryValueKey(KeyHandle,\r
-                                     &ValueName,\r
-                                     KeyValuePartialInformation,\r
-                                     &ValueData,\r
-                                     sizeof(ValueData),\r
-                                     &ResultLength);\r
-            if (NT_SUCCESS(Status))\r
-            {\r
-                if (ResultLength == sizeof(ValueData) &&\r
-                    ValueData.Info.Type == REG_DWORD)\r
-                {\r
-                    FastSystemCallDisable = *(PULONG)ValueData.Info.Data != 0;\r
-                }\r
-\r
-                ZwClose(KeyHandle);\r
-            }\r
-        }\r
-\r
-    } else {\r
-\r
-        /* Disable SYSENTER/SYSEXIT, because the CPU doesn't support it */\r
-        FastSystemCallDisable = 1;\r
-\r
-    }\r
-\r
-    if (FastSystemCallDisable) {\r
-        /* Use INT2E */\r
-        const unsigned char Entry[7] = {0x8D, 0x54, 0x24, 0x08,     /* lea    0x8(%esp),%edx    */\r
-                                        0xCD, 0x2E,                 /* int    0x2e              */\r
-                                        0xC3};                      /* ret                      */\r
-        memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));\r
-    } else {\r
-        /* Use SYSENTER */\r
-        const unsigned char Entry[5] = {0x8B, 0xD4,                 /* movl    %esp,%edx        */ \r
-                                        0x0F, 0x34,                 /* sysenter                 */\r
-                                        0xC3};                      /* ret                      */    \r
-        memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));\r
-        /* Enable SYSENTER/SYSEXIT */\r
-        KiFastSystemCallDisable = 0;\r
-    }\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KeFlushCurrentTb(VOID)\r
-{\r
-    /* Flush the TLB by resetting CR3 */\r
-    _Ke386SetCr(3, _Ke386GetCr(3));\r
-}\r
-\r
+/*
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            ntoskrnl/ke/i386/cpu.c
+ * PURPOSE:         Routines for CPU-level support
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@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 */
+KTSS KiBootTss;
+
+/* The TSS to use for Double Fault Traps (INT 0x9) */
+UCHAR KiDoubleFaultTSS[KTSS_IO_MAPS];
+
+/* The TSS to use for NMI Fault Traps (INT 0x2) */
+UCHAR KiNMITSS[KTSS_IO_MAPS];
+
+/* The Boot GDT */
+KGDTENTRY KiBootGdt[256] =
+{
+    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_NULL */
+    {0xffff, 0x0000, {{0x00, 0x9b, 0xcf, 0x00}}},       /* KGDT_R0_CODE */
+    {0xffff, 0x0000, {{0x00, 0x93, 0xcf, 0x00}}},       /* KGDT_R0_DATA */
+    {0xffff, 0x0000, {{0x00, 0xfb, 0xcf, 0x00}}},       /* KGDT_R3_CODE */
+    {0xffff, 0x0000, {{0x00, 0xf3, 0xcf, 0x00}}},       /* KGDT_R3_DATA*/
+    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_TSS */
+    {0x0fff, 0x0000, {{0x00, 0x93, 0xc0, 0xff}}},       /* KGDT_R0_PCR */
+    {0x0fff, 0x0000, {{0x00, 0xf3, 0x40, 0x00}}},       /* KGDT_R3_TEB */
+    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_UNUSED */
+    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_LDT */
+    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}},       /* KGDT_DF_TSS */
+    {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}}        /* KGDT_NMI_TSS */
+};
+
+/* GDT Descriptor */
+KDESCRIPTOR KiGdtDescriptor = {sizeof(KiBootGdt), (ULONG)KiBootGdt};
+
+/* CPU Features and Flags */
+ULONG KeI386CpuType;
+ULONG KeI386CpuStep;
+ULONG KeProcessorArchitecture;
+ULONG KeProcessorLevel;
+ULONG KeProcessorRevision;
+ULONG KeFeatureBits;
+ULONG KiFastSystemCallDisable = 1;
+ULONG KeI386NpxPresent = 0;
+ULONG KiMXCsrMask = 0;
+ULONG MxcsrFeatureMask = 0;
+ULONG KeI386XMMIPresent = 0;
+ULONG KeI386FxsrPresent = 0;
+ULONG KeI386MachineType;
+ULONG Ke386Pae = FALSE;
+ULONG Ke386NoExecute = FALSE;
+ULONG KeLargestCacheLine = 0x40;
+ULONG KeDcacheFlushCount = 0;
+ULONG KeIcacheFlushCount = 0;
+ULONG KiDmaIoCoherency = 0;
+CHAR KeNumberProcessors;
+KAFFINITY KeActiveProcessors = 1;
+BOOLEAN KiI386PentiumLockErrataPresent;
+BOOLEAN KiSMTProcessorsPresent;
+
+/* Freeze data */
+KIRQL KiOldIrql;
+ULONG KiFreezeFlag;
+
+/* 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(OUT ULONG CpuInfo[4],
+      IN ULONG InfoType)
+{
+    Ki386Cpuid(InfoType, &CpuInfo[0], &CpuInfo[1], &CpuInfo[2], &CpuInfo[3]);
+}
+
+VOID
+WRMSR(IN ULONG Register,
+      IN LONGLONG Value)
+{
+    LARGE_INTEGER LargeVal;
+    LargeVal.QuadPart = Value;
+    Ke386Wrmsr(Register, LargeVal.HighPart, LargeVal.LowPart);
+}
+
+LONGLONG
+RDMSR(IN ULONG Register)
+{
+    LARGE_INTEGER LargeVal;
+    Ke386Rdmsr(Register, LargeVal.HighPart, LargeVal.LowPart);
+    return LargeVal.QuadPart;
+}
+
+/* FUNCTIONS *****************************************************************/
+
+VOID
+NTAPI
+KiSetProcessorType(VOID)
+{
+    ULONG EFlags, NewEFlags;
+    ULONG Reg[4];
+    ULONG Stepping, Type;
+
+    /* Start by assuming no CPUID data */
+    KeGetCurrentPrcb()->CpuID = 0;
+
+    /* Save EFlags */
+    Ke386SaveFlags(EFlags);
+
+    /* XOR out the ID bit and update EFlags */
+    NewEFlags = EFlags ^ EFLAGS_ID;
+    Ke386RestoreFlags(NewEFlags);
+
+    /* Get them back and see if they were modified */
+    Ke386SaveFlags(NewEFlags);
+    if (NewEFlags != EFlags)
+    {
+        /* The modification worked, so CPUID exists. Set the ID Bit again. */
+        EFlags |= EFLAGS_ID;
+        Ke386RestoreFlags(EFlags);
+
+        /* Peform CPUID 0 to see if CPUID 1 is supported */
+        CPUID(Reg, 0);
+        if (Reg[0] > 0)
+        {
+            /* 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;
+        }
+        else
+        {
+            DPRINT1("CPUID Support lacking\n");
+        }
+    }
+    else
+    {
+        DPRINT1("CPUID Support lacking\n");
+    }
+
+    /* Restore EFLAGS */
+    Ke386RestoreFlags(EFlags);
+}
+
+ULONG
+NTAPI
+KiGetCpuVendor(VOID)
+{
+    PKPRCB Prcb = KeGetCurrentPrcb();
+    ULONG Vendor[5];
+    ULONG Temp;
+
+    /* Assume no Vendor ID and fail if no CPUID Support. */
+    Prcb->VendorString[0] = 0;
+    if (!Prcb->CpuID) return 0;
+
+    /* Get the Vendor ID and null-terminate it */
+    CPUID(Vendor, 0);
+    Vendor[4] = 0;
+
+    /* Re-arrange vendor string */
+    Temp = Vendor[2];
+    Vendor[2] = Vendor[3];
+    Vendor[3] = Temp;
+
+    /* Copy it to the PRCB and null-terminate it again */
+    RtlCopyMemory(Prcb->VendorString,
+                  &Vendor[1],
+                  sizeof(Prcb->VendorString) - sizeof(CHAR));
+    Prcb->VendorString[sizeof(Prcb->VendorString) - sizeof(CHAR)] = ANSI_NULL;
+
+    /* Now check the CPU Type */
+    if (!strcmp(Prcb->VendorString, CmpIntelID))
+    {
+        return CPU_INTEL;
+    }
+    else if (!strcmp(Prcb->VendorString, CmpAmdID))
+    {
+        return CPU_AMD;
+    }
+    else if (!strcmp(Prcb->VendorString, CmpCyrixID))
+    {
+        DPRINT1("Cyrix CPUs not fully supported\n");
+        return 0;
+    }
+    else if (!strcmp(Prcb->VendorString, CmpTransmetaID))
+    {
+        DPRINT1("Transmeta CPUs not fully supported\n");
+        return 0;
+    }
+    else if (!strcmp(Prcb->VendorString, CmpCentaurID))
+    {
+        DPRINT1("VIA CPUs not fully supported\n");
+        return 0;
+    }
+    else if (!strcmp(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;
+    ULONG Reg[4];
+    BOOLEAN ExtendedCPUID = TRUE;
+    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);
+
+    /* Check for AMD CPU */
+    if (Vendor == CPU_AMD)
+    {
+        /* Check if this is a K5 or higher. */
+        if ((Reg[0] & 0x0F00) >= 0x0500)
+        {
+            /* Check if this is a K5 specifically. */
+            if ((Reg[0] & 0x0F00) == 0x0500)
+            {
+                /* Get the Model Number */
+                switch (Reg[0] & 0x00F0)
+                {
+                    /* Check if this is the Model 1 */
+                    case 0x0010:
+
+                        /* Check if this is Step 0 or 1. They don't support PGE */
+                        if ((Reg[0] & 0x000F) > 0x03) break;
+
+                    case 0x0000:
+
+                        /* Model 0 doesn't support PGE at all. */
+                        Reg[3] &= ~0x2000;
+                        break;
+
+                    case 0x0080:
+
+                        /* K6-2, Step 8 and over have support for MTRR. */
+                        if ((Reg[0] & 0x000F) >= 0x8) FeatureBits |= KF_AMDK6MTRR;
+                        break;
+
+                    case 0x0090:
+
+                        /* As does the K6-3 */
+                        FeatureBits |= KF_AMDK6MTRR;
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+        }
+        else
+        {
+            /* Families below 5 don't support PGE, PSE or CMOV at all */
+            Reg[3] &= ~(0x08 | 0x2000 | 0x8000);
+
+            /* They also don't support advanced CPUID functions. */
+            ExtendedCPUID = FALSE;
+        }
+
+        /* Set the current features */
+        CpuFeatures = Reg[3];
+    }
+
+    /* Now check if this is Intel */
+    if (Vendor == CPU_INTEL)
+    {
+        /* Check if it's a P6 */
+        if (Prcb->CpuType == 6)
+        {
+            /* Perform the special sequence to get the MicroCode Signature */
+            WRMSR(0x8B, 0);
+            CPUID(Reg, 1);
+            Prcb->UpdateSignature.QuadPart = RDMSR(0x8B);
+        }
+        else if (Prcb->CpuType == 5)
+        {
+            /* On P5, enable workaround for the LOCK errata. */
+            KiI386PentiumLockErrataPresent = TRUE;
+        }
+
+        /* Check for broken P6 with bad SMP PTE implementation */
+        if (((Reg[0] & 0x0FF0) == 0x0610 && (Reg[0] & 0x000F) <= 0x9) ||
+            ((Reg[0] & 0x0FF0) == 0x0630 && (Reg[0] & 0x000F) <= 0x4))
+        {
+            /* Remove support for correct PTE support. */
+            FeatureBits &= ~KF_WORKING_PTE;
+        }
+
+        /* Check if the CPU is too old to support SYSENTER */
+        if ((Prcb->CpuType < 6) ||
+            ((Prcb->CpuType == 6) && (Prcb->CpuStep < 0x0303)))
+        {
+            /* Disable it */
+            Reg[3] &= ~0x800;
+        }
+
+        /* 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;
+
+    /* 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 if CPUID 0x80000000 is supported */
+    if (ExtendedCPUID)
+    {
+        /* Do the call */
+        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;
+    ULONG 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:
+
+        /* FIXME */
+        DPRINT1("Not handling AMD caches yet\n");
+        break;
+    }
+}
+
+VOID
+NTAPI
+KiSetCR0Bits(VOID)
+{
+    ULONG Cr0;
+
+    /* Save current CR0 */
+    Cr0 = __readcr0();
+
+    /* If this is a 486, enable Write-Protection */
+    if (KeGetCurrentPrcb()->CpuType > 3) Cr0 |= CR0_WP;
+
+    /* Set new Cr0 */
+    __writecr0(Cr0);
+}
+
+VOID
+NTAPI
+KiInitializeTSS2(IN PKTSS Tss,
+                 IN PKGDTENTRY TssEntry OPTIONAL)
+{
+    PUCHAR p;
+
+    /* Make sure the GDT Entry is valid */
+    if (TssEntry)
+    {
+        /* Set the Limit */
+        TssEntry->LimitLow = sizeof(KTSS) - 1;
+        TssEntry->HighWord.Bits.LimitHi = 0;
+    }
+
+    /* Now clear the I/O Map */
+    RtlFillMemory(Tss->IoMaps[0].IoMap, 8096, -1);
+
+    /* Initialize Interrupt Direction Maps */
+    p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);
+    RtlZeroMemory(p, 32);
+
+    /* Add DPMI support for interrupts */
+    p[0] = 4;
+    p[3] = 0x18;
+    p[4] = 0x18;
+
+    /* Initialize the default Interrupt Direction Map */
+    p = Tss->IntDirectionMap;
+    RtlZeroMemory(Tss->IntDirectionMap, 32);
+
+    /* Add DPMI support */
+    p[0] = 4;
+    p[3] = 0x18;
+    p[4] = 0x18;
+}
+
+VOID
+NTAPI
+KiInitializeTSS(IN PKTSS Tss)
+{
+    /* Set an invalid map base */
+    Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
+
+    /* Disable traps during Task Switches */
+    Tss->Flags = 0;
+
+    /* Set LDT and Ring 0 SS */
+    Tss->LDT = 0;
+    Tss->Ss0 = KGDT_R0_DATA;
+}
+
+VOID
+FASTCALL
+Ki386InitializeTss(IN PKTSS Tss,
+                   IN PKIDTENTRY Idt,
+                   IN PKGDTENTRY Gdt)
+{
+    PKGDTENTRY TssEntry, TaskGateEntry;
+
+    /* Initialize the boot TSS. */
+    TssEntry = &Gdt[KGDT_TSS / sizeof(KGDTENTRY)];
+    TssEntry->HighWord.Bits.Type = I386_TSS;
+    TssEntry->HighWord.Bits.Pres = 1;
+    TssEntry->HighWord.Bits.Dpl = 0;
+    KiInitializeTSS2(Tss, TssEntry);
+    KiInitializeTSS(Tss);
+
+    /* Load the task register */
+    Ke386SetTr(KGDT_TSS);
+
+    /* Setup the Task Gate for Double Fault Traps */
+    TaskGateEntry = (PKGDTENTRY)&Idt[8];
+    TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
+    TaskGateEntry->HighWord.Bits.Pres = 1;
+    TaskGateEntry->HighWord.Bits.Dpl = 0;
+    ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_DF_TSS;
+
+    /* Initialize the TSS used for handling double faults. */
+    Tss = (PKTSS)KiDoubleFaultTSS;
+    KiInitializeTSS(Tss);
+    Tss->CR3 = __readcr3();
+    Tss->Esp0 = PtrToUlong(KiDoubleFaultStack);
+    Tss->Eip = PtrToUlong(KiTrap8);
+    Tss->Cs = KGDT_R0_CODE;
+    Tss->Fs = KGDT_R0_PCR;
+    Tss->Ss = Ke386GetSs();
+    Tss->Es = KGDT_R3_DATA | RPL_MASK;
+    Tss->Ds = KGDT_R3_DATA | RPL_MASK;
+
+    /* Setup the Double Trap TSS entry in the GDT */
+    TssEntry = &Gdt[KGDT_DF_TSS / sizeof(KGDTENTRY)];
+    TssEntry->HighWord.Bits.Type = I386_TSS;
+    TssEntry->HighWord.Bits.Pres = 1;
+    TssEntry->HighWord.Bits.Dpl = 0;
+    TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
+    TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
+    TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
+    TssEntry->LimitLow = KTSS_IO_MAPS;
+
+    /* Now setup the NMI Task Gate */
+    TaskGateEntry = (PKGDTENTRY)&Idt[2];
+    TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
+    TaskGateEntry->HighWord.Bits.Pres = 1;
+    TaskGateEntry->HighWord.Bits.Dpl = 0;
+    ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_NMI_TSS;
+
+    /* Initialize the actual TSS */
+    Tss = (PKTSS)KiNMITSS;
+    KiInitializeTSS(Tss);
+    Tss->CR3 = __readcr3();
+    Tss->Esp0 = PtrToUlong(KiDoubleFaultStack);
+    Tss->Eip = PtrToUlong(KiTrap2);
+    Tss->Cs = KGDT_R0_CODE;
+    Tss->Fs = KGDT_R0_PCR;
+    Tss->Ss = Ke386GetSs();
+    Tss->Es = KGDT_R3_DATA | RPL_MASK;
+    Tss->Ds = KGDT_R3_DATA | RPL_MASK;
+
+    /* And its associated TSS Entry */
+    TssEntry = &Gdt[KGDT_NMI_TSS / sizeof(KGDTENTRY)];
+    TssEntry->HighWord.Bits.Type = I386_TSS;
+    TssEntry->HighWord.Bits.Pres = 1;
+    TssEntry->HighWord.Bits.Dpl = 0;
+    TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
+    TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
+    TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
+    TssEntry->LimitLow = KTSS_IO_MAPS;
+}
+
+VOID
+NTAPI
+KeFlushCurrentTb(VOID)
+{
+    /* Flush the TLB by resetting CR3 */
+    __writecr3((ULONGLONG)__readcr3());
+}
+
+VOID
+NTAPI
+KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState)
+{
+    return;
+    /* Restore the CR registers */
+    __writecr0(ProcessorState->SpecialRegisters.Cr0);
+    Ke386SetCr2(ProcessorState->SpecialRegisters.Cr2);
+    __writecr3(ProcessorState->SpecialRegisters.Cr3);
+    if (KeFeatureBits & KF_CR4) __writecr4(ProcessorState->SpecialRegisters.Cr4);
+
+    //
+    // Restore the DR registers
+    //
+    Ke386SetDr0(ProcessorState->SpecialRegisters.KernelDr0);
+    Ke386SetDr1(ProcessorState->SpecialRegisters.KernelDr1);
+    Ke386SetDr2(ProcessorState->SpecialRegisters.KernelDr2);
+    Ke386SetDr3(ProcessorState->SpecialRegisters.KernelDr3);
+    Ke386SetDr6(ProcessorState->SpecialRegisters.KernelDr6);
+    Ke386SetDr7(ProcessorState->SpecialRegisters.KernelDr7);
+
+    //
+    // Restore GDT, IDT, LDT and TSS
+    //
+    Ke386SetGlobalDescriptorTable(ProcessorState->SpecialRegisters.Gdtr);
+    Ke386SetInterruptDescriptorTable(ProcessorState->SpecialRegisters.Idtr);
+    Ke386SetTr(ProcessorState->SpecialRegisters.Tr);
+    Ke386SetLocalDescriptorTable(ProcessorState->SpecialRegisters.Ldtr);
+}
+
+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 = (KeFeatureBits & KF_CR4) ?
+                                           __readcr4() : 0;
+
+    /* Save the DR registers */
+    ProcessorState->SpecialRegisters.KernelDr0 = Ke386GetDr0();
+    ProcessorState->SpecialRegisters.KernelDr1 = Ke386GetDr1();
+    ProcessorState->SpecialRegisters.KernelDr2 = Ke386GetDr2();
+    ProcessorState->SpecialRegisters.KernelDr3 = Ke386GetDr3();
+    ProcessorState->SpecialRegisters.KernelDr6 = Ke386GetDr6();
+    ProcessorState->SpecialRegisters.KernelDr7 = Ke386GetDr7();
+    Ke386SetDr7(0);
+
+    /* Save GDT, IDT, LDT and TSS */
+    Ke386GetGlobalDescriptorTable(ProcessorState->SpecialRegisters.Gdtr);
+    Ke386GetInterruptDescriptorTable(ProcessorState->SpecialRegisters.Idtr);
+    Ke386GetTr(ProcessorState->SpecialRegisters.Tr);
+    Ke386GetLocalDescriptorTable(ProcessorState->SpecialRegisters.Ldtr);
+}
+
+VOID
+NTAPI
+KiInitializeMachineType(VOID)
+{
+    /* Set the Machine Type we got from NTLDR */
+    KeI386MachineType = KeLoaderBlock->u.I386.MachineType & 0x000FF;
+}
+
+ULONG_PTR
+NTAPI
+KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
+{
+    /* Set CS and ESP */
+    Ke386Wrmsr(0x174, KGDT_R0_CODE, 0);
+    Ke386Wrmsr(0x175, KeGetCurrentPrcb()->DpcStack, 0);
+
+    /* Set LSTAR */
+    Ke386Wrmsr(0x176, KiFastCallEntry, 0);
+    return 0;
+}
+
+VOID
+NTAPI
+KiRestoreFastSyscallReturnState(VOID)
+{
+    /* FIXME: NT has support for SYSCALL, IA64-SYSENTER, etc. */
+
+    /* Check if the CPU Supports fast system call */
+    if (KeFeatureBits & KF_FAST_SYSCALL)
+    {
+        /* Do an IPI to enable it */
+        KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters, 0);
+    }
+}
+
+ULONG_PTR
+NTAPI
+Ki386EnableDE(IN ULONG_PTR Context)
+{
+    /* Enable DE */
+    __writecr4(__readcr4() | CR4_DE);
+    return 0;
+}
+
+ULONG_PTR
+NTAPI
+Ki386EnableFxsr(IN ULONG_PTR Context)
+{
+    /* Enable FXSR */
+    __writecr4(__readcr4() | CR4_FXSR);
+    return 0;
+}
+
+ULONG_PTR
+NTAPI
+Ki386EnableXMMIExceptions(IN ULONG_PTR Context)
+{
+#if 0 // needs kitrap13
+    PKIDTENTRY IdtEntry;
+
+    /* Get the IDT Entry for Interrupt 19 */
+    IdtEntry = ((PKIPCR)KeGetPcr())->IDT[19];
+
+    /* Set it up */
+    IdtEntry->Selector = KGDT_R0_CODE;
+    IdtEntry->Offset = (KiTrap13 & 0xFFFF);
+    IdtEntry->ExtendedOffset = (KiTrap13 >> 16) & 0xFFFF;
+    ((PKIDT_ACCESS)&IdtEntry->Access)->Dpl = 0;
+    ((PKIDT_ACCESS)&IdtEntry->Access)->Present = 1;
+    ((PKIDT_ACCESS)&IdtEntry->Access)->SegmentType = I386_INTERRUPT_GATE;
+#endif
+
+    /* Enable XMMI exceptions */
+    __writecr4(__readcr4() | CR4_XMMEXCPT);
+    return 0;
+}
+
+VOID
+NTAPI
+KiI386PentiumLockErrataFixup(VOID)
+{
+    KDESCRIPTOR IdtDescriptor;
+    PKIDTENTRY NewIdt, NewIdt2;
+
+    /* Allocate memory for a new IDT */
+    NewIdt = ExAllocatePool(NonPagedPool, 2 * PAGE_SIZE);
+
+    /* Put everything after the first 7 entries on a new page */
+    NewIdt2 = (PVOID)((ULONG_PTR)NewIdt + PAGE_SIZE - (7 * sizeof(KIDTENTRY)));
+
+    /* Disable interrupts */
+    _disable();
+
+    /* Get the current IDT and copy it */
+    Ke386GetInterruptDescriptorTable(IdtDescriptor);
+    RtlCopyMemory(NewIdt2,
+                  (PVOID)IdtDescriptor.Base,
+                  IdtDescriptor.Limit + 1);
+    IdtDescriptor.Base = (ULONG)NewIdt2;
+
+    /* Set the new IDT */
+    Ke386SetInterruptDescriptorTable(IdtDescriptor);
+    ((PKIPCR)KeGetPcr())->IDT = NewIdt2;
+
+    /* Restore interrupts */
+    _enable();
+
+    /* Set the first 7 entries as read-only to produce a fault */
+    MmSetPageProtect(NULL, NewIdt, PAGE_READONLY);
+}
+
+BOOLEAN
+NTAPI
+KeFreezeExecution(IN PKTRAP_FRAME TrapFrame,
+                  IN PKEXCEPTION_FRAME ExceptionFrame)
+{
+    ULONG Flags;
+
+    /* Disable interrupts and get previous state */
+    Ke386SaveFlags(Flags);
+    //Flags = __getcallerseflags();
+    _disable();
+
+    /* Save freeze flag */
+    KiFreezeFlag = 4;
+
+    /* Save the old IRQL */
+    KiOldIrql = KeGetCurrentIrql();
+
+    /* Return whether interrupts were enabled */
+    return (Flags & EFLAGS_INTERRUPT_MASK) ? TRUE: FALSE;
+}
+
+VOID
+NTAPI
+KeThawExecution(IN BOOLEAN Enable)
+{
+    /* Cleanup CPU caches */
+    KeFlushCurrentTb();
+
+    /* Re-enable interrupts */
+    if (Enable) _enable();
+}
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)
+{
+    PFNSAVE_FORMAT FpState;
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+    DPRINT1("%s is not really implemented\n", __FUNCTION__);
+
+    /* check if we are doing software emulation */
+    if (!KeI386NpxPresent) return STATUS_ILLEGAL_FLOAT_CONTEXT;
+
+    FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT));
+    if (!FpState) return STATUS_INSUFFICIENT_RESOURCES;
+
+    *((PVOID *) Save) = FpState;
+#ifdef __GNUC__
+    asm volatile("fnsave %0\n\t" : "=m" (*FpState));
+#else
+    __asm
+    {
+        fnsave [FpState]
+    };
+#endif
+
+    KeGetCurrentThread()->DispatcherHeader.NpxIrql = KeGetCurrentIrql();
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save)
+{
+    PFNSAVE_FORMAT FpState = *((PVOID *) Save);
+    ASSERT(KeGetCurrentThread()->DispatcherHeader.NpxIrql == KeGetCurrentIrql());
+    DPRINT1("%s is not really implemented\n", __FUNCTION__);
+
+#ifdef __GNUC__
+    asm volatile("fnclex\n\t");
+    asm volatile("frstor %0\n\t" : "=m" (*FpState));
+#else
+    __asm
+    {
+        fnclex
+        frstor [FpState]
+    };
+#endif
+
+    ExFreePool(FpState);
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+ULONG
+NTAPI
+KeGetRecommendedSharedDataAlignment(VOID)
+{
+    /* Return the global variable */
+    return KeLargestCacheLine;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+KeFlushEntireTb(IN BOOLEAN Invalid,
+                IN BOOLEAN AllProcessors)
+{
+    KIRQL OldIrql;
+
+    /* Raise the IRQL for the TB Flush */
+    OldIrql = KeRaiseIrqlToSynchLevel();
+
+#ifdef CONFIG_SMP
+    /* FIXME: Support IPI Flush */
+#error Not yet implemented!
+#endif
+
+    /* Flush the TB for the Current CPU */
+    KeFlushCurrentTb();
+
+    /* Return to Original IRQL */
+    KeLowerIrql(OldIrql);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+KeSetDmaIoCoherency(IN ULONG Coherency)
+{
+    /* Save the coherency globally */
+    KiDmaIoCoherency = Coherency;
+}
+
+/*
+ * @implemented
+ */
+KAFFINITY
+NTAPI
+KeQueryActiveProcessors(VOID)
+{
+    PAGED_CODE();
+
+    /* Simply return the number of active processors */
+    return KeActiveProcessors;
+}
+
+/*
+ * @implemented
+ */
+VOID
+__cdecl
+KeSaveStateForHibernate(IN PKPROCESSOR_STATE State)
+{
+    /* Capture the context */
+    RtlCaptureContext(&State->ContextFrame);
+
+    /* Capture the control state */
+    KiSaveProcessorControlState(State);
+}