-/*\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);
+}