2006-11-08 11:47:44 +00:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
|
|
|
|
/* 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];
|
|
|
|
|
|
|
|
/* CPU Features and Flags */
|
|
|
|
ULONG KeI386CpuType;
|
|
|
|
ULONG KeI386CpuStep;
|
|
|
|
ULONG KeProcessorArchitecture;
|
|
|
|
ULONG KeProcessorLevel;
|
|
|
|
ULONG KeProcessorRevision;
|
|
|
|
ULONG KeFeatureBits;
|
2010-01-26 19:39:21 +00:00
|
|
|
ULONG KiFastSystemCallDisable;
|
2006-11-08 11:47:44 +00:00
|
|
|
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;
|
2010-01-28 20:43:25 +00:00
|
|
|
ULONG KePrefetchNTAGranularity = 32;
|
2006-11-08 11:47:44 +00:00
|
|
|
CHAR KeNumberProcessors;
|
|
|
|
KAFFINITY KeActiveProcessors = 1;
|
|
|
|
BOOLEAN KiI386PentiumLockErrataPresent;
|
|
|
|
BOOLEAN KiSMTProcessorsPresent;
|
|
|
|
|
2010-01-27 03:05:10 +00:00
|
|
|
/* The distance between SYSEXIT and IRETD return modes */
|
|
|
|
UCHAR KiSystemCallExitAdjust;
|
|
|
|
|
|
|
|
/* The offset that was applied -- either 0 or the value above */
|
|
|
|
UCHAR KiSystemCallExitAdjusted;
|
|
|
|
|
|
|
|
/* Whether the adjustment was already done once */
|
|
|
|
BOOLEAN KiFastCallCopyDoneOnce;
|
|
|
|
|
2007-09-26 16:41:35 +00:00
|
|
|
/* Flush data */
|
|
|
|
volatile LONG KiTbFlushTimeStamp;
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/* 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
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(IN ULONG InfoType,
|
|
|
|
OUT PULONG CpuInfoEax,
|
|
|
|
OUT PULONG CpuInfoEbx,
|
|
|
|
OUT PULONG CpuInfoEcx,
|
|
|
|
OUT PULONG CpuInfoEdx)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2009-09-27 10:09:38 +00:00
|
|
|
ULONG CpuInfo[4];
|
|
|
|
|
2009-09-21 15:20:18 +00:00
|
|
|
/* Perform the CPUID Operation */
|
|
|
|
__cpuid((int*)CpuInfo, InfoType);
|
2009-09-27 10:09:38 +00:00
|
|
|
|
|
|
|
/* Return the results */
|
|
|
|
*CpuInfoEax = CpuInfo[0];
|
|
|
|
*CpuInfoEbx = CpuInfo[1];
|
|
|
|
*CpuInfoEcx = CpuInfo[2];
|
|
|
|
*CpuInfoEdx = CpuInfo[3];
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
2009-09-21 15:20:18 +00:00
|
|
|
NTAPI
|
2006-11-08 11:47:44 +00:00
|
|
|
WRMSR(IN ULONG Register,
|
|
|
|
IN LONGLONG Value)
|
|
|
|
{
|
2009-09-21 15:20:18 +00:00
|
|
|
/* Write to the MSR */
|
|
|
|
__writemsr(Register, Value);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LONGLONG
|
2009-09-27 10:09:38 +00:00
|
|
|
FASTCALL
|
2006-11-08 11:47:44 +00:00
|
|
|
RDMSR(IN ULONG Register)
|
|
|
|
{
|
2009-09-21 15:20:18 +00:00
|
|
|
/* Read from the MSR */
|
|
|
|
return __readmsr(Register);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiSetProcessorType(VOID)
|
|
|
|
{
|
2009-09-21 15:20:18 +00:00
|
|
|
ULONG EFlags, NewEFlags;
|
2009-09-27 10:09:38 +00:00
|
|
|
ULONG Reg, Dummy;
|
2006-11-08 11:47:44 +00:00
|
|
|
ULONG Stepping, Type;
|
|
|
|
|
|
|
|
/* Start by assuming no CPUID data */
|
|
|
|
KeGetCurrentPrcb()->CpuID = 0;
|
|
|
|
|
|
|
|
/* Save EFlags */
|
2009-09-21 15:20:18 +00:00
|
|
|
EFlags = __readeflags();
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* XOR out the ID bit and update EFlags */
|
2008-08-18 17:05:31 +00:00
|
|
|
NewEFlags = EFlags ^ EFLAGS_ID;
|
2009-09-21 15:20:18 +00:00
|
|
|
__writeeflags(NewEFlags);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Get them back and see if they were modified */
|
2009-09-21 15:20:18 +00:00
|
|
|
NewEFlags = __readeflags();
|
2006-11-08 11:47:44 +00:00
|
|
|
if (NewEFlags != EFlags)
|
|
|
|
{
|
|
|
|
/* The modification worked, so CPUID exists. Set the ID Bit again. */
|
2008-08-18 17:05:31 +00:00
|
|
|
EFlags |= EFLAGS_ID;
|
2009-09-21 15:20:18 +00:00
|
|
|
__writeeflags(EFlags);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Peform CPUID 0 to see if CPUID 1 is supported */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(0, &Reg, &Dummy, &Dummy, &Dummy);
|
|
|
|
if (Reg > 0)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
|
|
|
/* Do CPUID 1 now */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(1, &Reg, &Dummy, &Dummy, &Dummy);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2009-09-27 10:09:38 +00:00
|
|
|
Stepping = Reg & 0xF0;
|
2006-11-08 11:47:44 +00:00
|
|
|
Stepping <<= 4;
|
2009-09-27 10:09:38 +00:00
|
|
|
Stepping += (Reg & 0xFF);
|
2006-11-08 11:47:44 +00:00
|
|
|
Stepping &= 0xF0F;
|
2009-09-27 10:09:38 +00:00
|
|
|
Type = Reg & 0xF00;
|
2006-11-08 11:47:44 +00:00
|
|
|
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 */
|
2009-09-21 15:20:18 +00:00
|
|
|
__writeeflags(EFlags);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(0, &Vendor[0], &Vendor[1], &Vendor[2], &Vendor[3]);
|
2006-11-08 11:47:44 +00:00
|
|
|
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))
|
|
|
|
{
|
2008-10-02 21:57:36 +00:00
|
|
|
DPRINT1("Cyrix CPU support not fully tested!\n");
|
|
|
|
return CPU_CYRIX;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
else if (!strcmp(Prcb->VendorString, CmpTransmetaID))
|
|
|
|
{
|
2008-10-02 21:57:36 +00:00
|
|
|
DPRINT1("Transmeta CPU support not fully tested!\n");
|
|
|
|
return CPU_TRANSMETA;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
else if (!strcmp(Prcb->VendorString, CmpCentaurID))
|
|
|
|
{
|
2008-10-02 21:57:36 +00:00
|
|
|
DPRINT1("Centaur CPU support not fully tested!\n");
|
|
|
|
return CPU_CENTAUR;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
else if (!strcmp(Prcb->VendorString, CmpRiseID))
|
|
|
|
{
|
2008-10-02 21:57:36 +00:00
|
|
|
DPRINT1("Rise CPU support not fully tested!\n");
|
|
|
|
return CPU_RISE;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Invalid CPU */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
|
|
NTAPI
|
|
|
|
KiGetFeatureBits(VOID)
|
|
|
|
{
|
|
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
|
|
ULONG Vendor;
|
|
|
|
ULONG FeatureBits = KF_WORKING_PTE;
|
2009-09-27 10:09:38 +00:00
|
|
|
ULONG Reg[4], Dummy;
|
2006-11-08 11:47:44 +00:00
|
|
|
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]. */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(1, &Reg[0], &Reg[1], &Dummy, &Reg[3]);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Set the initial APIC ID */
|
|
|
|
Prcb->InitialApicId = (UCHAR)(Reg[1] >> 24);
|
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
switch (Vendor)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Intel CPUs */
|
|
|
|
case CPU_INTEL:
|
2009-09-27 10:09:38 +00:00
|
|
|
|
2009-10-04 16:53:15 +00:00
|
|
|
/* Check if it's a P6 */
|
2008-10-02 21:57:36 +00:00
|
|
|
if (Prcb->CpuType == 6)
|
|
|
|
{
|
|
|
|
/* Perform the special sequence to get the MicroCode Signature */
|
|
|
|
WRMSR(0x8B, 0);
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(1, &Dummy, &Dummy, &Dummy, &Dummy);
|
2008-10-02 21:57:36 +00:00
|
|
|
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];
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* AMD CPUs */
|
|
|
|
case CPU_AMD:
|
|
|
|
|
|
|
|
/* Check if this is a K5 or K6. (family 5) */
|
2006-11-08 11:47:44 +00:00
|
|
|
if ((Reg[0] & 0x0F00) == 0x0500)
|
|
|
|
{
|
|
|
|
/* Get the Model Number */
|
|
|
|
switch (Reg[0] & 0x00F0)
|
|
|
|
{
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Model 1: K5 - 5k86 (initial models) */
|
2006-11-08 11:47:44 +00:00
|
|
|
case 0x0010:
|
|
|
|
|
|
|
|
/* Check if this is Step 0 or 1. They don't support PGE */
|
|
|
|
if ((Reg[0] & 0x000F) > 0x03) break;
|
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Model 0: K5 - SSA5 */
|
2006-11-08 11:47:44 +00:00
|
|
|
case 0x0000:
|
|
|
|
|
|
|
|
/* Model 0 doesn't support PGE at all. */
|
|
|
|
Reg[3] &= ~0x2000;
|
|
|
|
break;
|
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Model 8: K6-2 */
|
2006-11-08 11:47:44 +00:00
|
|
|
case 0x0080:
|
|
|
|
|
|
|
|
/* K6-2, Step 8 and over have support for MTRR. */
|
|
|
|
if ((Reg[0] & 0x000F) >= 0x8) FeatureBits |= KF_AMDK6MTRR;
|
|
|
|
break;
|
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Model 9: K6-III
|
|
|
|
Model D: K6-2+, K6-III+ */
|
2006-11-08 11:47:44 +00:00
|
|
|
case 0x0090:
|
2008-10-02 21:57:36 +00:00
|
|
|
case 0x00D0:
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
FeatureBits |= KF_AMDK6MTRR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-10-02 21:57:36 +00:00
|
|
|
else if((Reg[0] & 0x0F00) < 0x0500)
|
|
|
|
{
|
|
|
|
/* Families below 5 don't support PGE, PSE or CMOV at all */
|
|
|
|
Reg[3] &= ~(0x08 | 0x2000 | 0x8000);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* They also don't support advanced CPUID functions. */
|
|
|
|
ExtendedCPUID = FALSE;
|
|
|
|
}
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Set the current features */
|
|
|
|
CpuFeatures = Reg[3];
|
2007-01-18 06:23:14 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
break;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Cyrix CPUs */
|
|
|
|
case CPU_CYRIX:
|
2009-09-27 10:09:38 +00:00
|
|
|
|
|
|
|
/* FIXME: CMPXCGH8B */
|
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
break;
|
2008-10-01 11:54:29 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Transmeta CPUs */
|
|
|
|
case CPU_TRANSMETA:
|
2009-09-27 10:09:38 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* Enable CMPXCHG8B if the family (>= 5), model and stepping (>= 4.2) support it */
|
|
|
|
if ((Reg[0] & 0x0FFF) >= 0x0542)
|
2008-10-01 18:19:42 +00:00
|
|
|
{
|
2008-10-02 21:57:36 +00:00
|
|
|
WRMSR(0x80860004, RDMSR(0x80860004) | 0x0100);
|
2008-10-01 18:19:42 +00:00
|
|
|
FeatureBits |= KF_CMPXCHG8B;
|
|
|
|
}
|
2008-10-01 11:54:29 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Centaur, IDT, Rise and VIA CPUs */
|
|
|
|
case CPU_CENTAUR:
|
|
|
|
case CPU_RISE:
|
2009-09-27 10:09:38 +00:00
|
|
|
|
2008-10-02 21:57:36 +00:00
|
|
|
/* These CPUs don't report the presence of CMPXCHG8B through CPUID.
|
|
|
|
However, this feature exists and operates properly without any additional steps. */
|
|
|
|
FeatureBits |= KF_CMPXCHG8B;
|
|
|
|
|
|
|
|
break;
|
2008-10-01 18:19:42 +00:00
|
|
|
}
|
2008-10-01 11:54:29 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/* 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;
|
2007-01-18 06:23:14 +00:00
|
|
|
if (CpuFeatures & 0x00200000) FeatureBits |= KF_DTS;
|
2006-11-08 11:47:44 +00:00
|
|
|
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 */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(0x80000000, &Reg[0], &Dummy, &Dummy, &Dummy);
|
2006-11-08 11:47:44 +00:00
|
|
|
if ((Reg[0] & 0xffffff00) == 0x80000000)
|
|
|
|
{
|
|
|
|
/* Check if CPUID 0x80000001 is supported */
|
|
|
|
if (Reg[0] >= 0x80000001)
|
|
|
|
{
|
|
|
|
/* Check which extended features are available. */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(0x80000001, &Dummy, &Dummy, &Dummy, &Reg[3]);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2007-01-18 06:23:14 +00:00
|
|
|
/* Check if NX-bit is supported */
|
|
|
|
if (Reg[3] & 0x00100000) FeatureBits |= KF_NX_BIT;
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/* Now handle each features for each CPU Vendor */
|
|
|
|
switch (Vendor)
|
|
|
|
{
|
|
|
|
case CPU_AMD:
|
2008-10-02 21:57:36 +00:00
|
|
|
case CPU_CENTAUR:
|
2006-11-08 11:47:44 +00:00
|
|
|
if (Reg[3] & 0x80000000) FeatureBits |= KF_3DNOW;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the Feature Bits */
|
|
|
|
return FeatureBits;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiGetCacheInformation(VOID)
|
|
|
|
{
|
|
|
|
PKIPCR Pcr = (PKIPCR)KeGetPcr();
|
|
|
|
ULONG Vendor;
|
2009-09-27 10:09:38 +00:00
|
|
|
ULONG Data[4], Dummy;
|
2006-11-08 11:47:44 +00:00
|
|
|
ULONG CacheRequests = 0, i;
|
|
|
|
ULONG CurrentRegister;
|
|
|
|
UCHAR RegisterByte;
|
2010-01-28 20:43:25 +00:00
|
|
|
ULONG Size, Associativity = 0, CacheLine = 64, CurrentSize = 0;
|
2006-11-08 11:47:44 +00:00
|
|
|
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 */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(0, &Data[0], &Dummy, &Dummy, &Dummy);
|
2006-11-08 11:47:44 +00:00
|
|
|
if (Data[0] >= 2)
|
|
|
|
{
|
|
|
|
/* We need to loop for the number of times CPUID will tell us to */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Do the CPUID call */
|
2009-09-27 10:09:38 +00:00
|
|
|
CPUID(2, &Data[0], &Data[1], &Data[2], &Data[3]);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
2010-01-28 20:43:25 +00:00
|
|
|
if (((RegisterByte > 0x40) && (RegisterByte <= 0x47)) ||
|
|
|
|
((RegisterByte > 0x78) && (RegisterByte <= 0x7C)) ||
|
|
|
|
((RegisterByte > 0x80) && (RegisterByte <= 0x85)))
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2010-01-28 20:43:25 +00:00
|
|
|
/* Compute associativity */
|
|
|
|
Associativity = 4;
|
|
|
|
if (RegisterByte >= 0x79) Associativity = 8;
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/* Mask out only the first nibble */
|
2010-01-28 20:43:25 +00:00
|
|
|
RegisterByte &= 0x07;
|
|
|
|
|
|
|
|
/* Check if this cache is bigger than the last */
|
|
|
|
Size = 0x10000 << RegisterByte;
|
|
|
|
if ((Size / Associativity) > CurrentSize)
|
|
|
|
{
|
|
|
|
/* Set the L2 Cache Size and Associativity */
|
|
|
|
CurrentSize = Size / Associativity;
|
|
|
|
Pcr->SecondLevelCacheSize = Size;
|
|
|
|
Pcr->SecondLevelCacheAssociativity = Associativity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((RegisterByte > 0x21) && (RegisterByte <= 0x29))
|
|
|
|
{
|
|
|
|
/* Set minimum cache line size */
|
|
|
|
if (CacheLine < 128) CacheLine = 128;
|
|
|
|
|
|
|
|
/* Hard-code size/associativity */
|
|
|
|
Associativity = 8;
|
|
|
|
switch (RegisterByte)
|
|
|
|
{
|
|
|
|
case 0x22:
|
|
|
|
Size = 512 * 1024;
|
|
|
|
Associativity = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x23:
|
|
|
|
Size = 1024 * 1024;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x25:
|
|
|
|
Size = 2048 * 1024;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x29:
|
|
|
|
Size = 4096 * 1024;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Size = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this cache is bigger than the last */
|
|
|
|
if ((Size / Associativity) > CurrentSize)
|
|
|
|
{
|
|
|
|
/* Set the L2 Cache Size and Associativity */
|
|
|
|
CurrentSize = Size / Associativity;
|
|
|
|
Pcr->SecondLevelCacheSize = Size;
|
|
|
|
Pcr->SecondLevelCacheAssociativity = Associativity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (((RegisterByte > 0x65) && (RegisterByte < 0x69)) ||
|
|
|
|
(RegisterByte == 0x2C) || (RegisterByte == 0xF0))
|
|
|
|
{
|
|
|
|
/* Indicates L1 cache line of 64 bytes */
|
|
|
|
KePrefetchNTAGranularity = 64;
|
|
|
|
}
|
|
|
|
else if (RegisterByte == 0xF1)
|
|
|
|
{
|
|
|
|
/* Indicates L1 cache line of 128 bytes */
|
|
|
|
KePrefetchNTAGranularity = 128;
|
|
|
|
}
|
|
|
|
else if (((RegisterByte >= 0x4A) && (RegisterByte <= 0x4C)) ||
|
|
|
|
(RegisterByte == 0x78) ||
|
|
|
|
(RegisterByte == 0x7D) ||
|
|
|
|
(RegisterByte == 0x7F) ||
|
|
|
|
(RegisterByte == 0x86) ||
|
|
|
|
(RegisterByte == 0x87))
|
|
|
|
{
|
|
|
|
/* Set minimum cache line size */
|
|
|
|
if (CacheLine < 64) CacheLine = 64;
|
|
|
|
|
|
|
|
/* Hard-code size/associativity */
|
|
|
|
switch (RegisterByte)
|
|
|
|
{
|
|
|
|
case 0x4A:
|
|
|
|
Size = 4 * 1024 * 1024;
|
|
|
|
Associativity = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4B:
|
|
|
|
Size = 6 * 1024 * 1024;
|
|
|
|
Associativity = 12;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4C:
|
|
|
|
Size = 8 * 1024 * 1024;
|
|
|
|
Associativity = 16;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x78:
|
|
|
|
Size = 1 * 1024 * 1024;
|
|
|
|
Associativity = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x7D:
|
|
|
|
Size = 2 * 1024 * 1024;
|
|
|
|
Associativity = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x7F:
|
|
|
|
Size = 512 * 1024;
|
|
|
|
Associativity = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x86:
|
|
|
|
Size = 512 * 1024;
|
|
|
|
Associativity = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x87:
|
|
|
|
Size = 1 * 1024 * 1024;
|
|
|
|
Associativity = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Size = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this cache is bigger than the last */
|
|
|
|
if ((Size / Associativity) > CurrentSize)
|
|
|
|
{
|
|
|
|
/* Set the L2 Cache Size and Associativity */
|
|
|
|
CurrentSize = Size / Associativity;
|
|
|
|
Pcr->SecondLevelCacheSize = Size;
|
|
|
|
Pcr->SecondLevelCacheAssociativity = Associativity;
|
|
|
|
}
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (--CacheRequests);
|
|
|
|
}
|
2007-05-26 16:37:11 +00:00
|
|
|
break;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2007-05-26 16:37:11 +00:00
|
|
|
case CPU_AMD:
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2010-01-28 20:43:25 +00:00
|
|
|
/* Check if we support CPUID 0x80000005 */
|
|
|
|
CPUID(0x80000000, &Data[0], &Data[1], &Data[2], &Data[3]);
|
|
|
|
if (Data[0] >= 0x80000006)
|
2007-05-26 16:37:11 +00:00
|
|
|
{
|
2010-01-28 20:43:25 +00:00
|
|
|
/* Get L1 size first */
|
|
|
|
CPUID(0x80000005, &Data[0], &Data[1], &Data[2], &Data[3]);
|
|
|
|
KePrefetchNTAGranularity = Data[2] & 0xFF;
|
|
|
|
|
|
|
|
/* Check if we support CPUID 0x80000006 */
|
|
|
|
CPUID(0x80000000, &Data[0], &Data[1], &Data[2], &Data[3]);
|
|
|
|
if (Data[0] >= 0x80000006)
|
|
|
|
{
|
|
|
|
/* Get 2nd level cache and tlb size */
|
|
|
|
CPUID(0x80000006, &Data[0], &Data[1], &Data[2], &Data[3]);
|
|
|
|
|
|
|
|
/* Cache line size */
|
|
|
|
CacheLine = Data[2] & 0xFF;
|
|
|
|
|
|
|
|
/* Hardcode associativity */
|
|
|
|
RegisterByte = Data[2] >> 12;
|
|
|
|
switch (RegisterByte)
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
Associativity = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
Associativity = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
Associativity = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
case 15:
|
|
|
|
Associativity = 16;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Associativity = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute size */
|
|
|
|
Size = (Data[2] >> 16) << 10;
|
|
|
|
|
|
|
|
/* Hack for Model 6, Steping 300 */
|
|
|
|
if ((KeGetCurrentPrcb()->CpuType == 6) &&
|
|
|
|
(KeGetCurrentPrcb()->CpuStep == 0x300))
|
|
|
|
{
|
|
|
|
/* Stick 64K in there */
|
|
|
|
Size = 64 * 1024;
|
|
|
|
}
|
2007-05-26 16:37:11 +00:00
|
|
|
|
2010-01-28 20:43:25 +00:00
|
|
|
/* Set the L2 Cache Size and associativity */
|
|
|
|
Pcr->SecondLevelCacheSize = Size;
|
|
|
|
Pcr->SecondLevelCacheAssociativity = Associativity;
|
|
|
|
}
|
2007-05-26 16:37:11 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-09-27 10:09:38 +00:00
|
|
|
|
|
|
|
case CPU_CYRIX:
|
|
|
|
case CPU_TRANSMETA:
|
|
|
|
case CPU_CENTAUR:
|
|
|
|
case CPU_RISE:
|
|
|
|
|
|
|
|
/* FIXME */
|
|
|
|
break;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
2010-01-28 20:43:25 +00:00
|
|
|
|
|
|
|
/* Set the cache line */
|
|
|
|
if (CacheLine > KeLargestCacheLine) KeLargestCacheLine = CacheLine;
|
|
|
|
DPRINT1("Prefetch Cache: %d bytes\tL2 Cache: %d bytes\tL2 Cache Line: %d bytes\tL2 Cache Associativity: %d\n",
|
|
|
|
KePrefetchNTAGranularity,
|
|
|
|
Pcr->SecondLevelCacheSize,
|
|
|
|
KeLargestCacheLine,
|
|
|
|
Pcr->SecondLevelCacheAssociativity);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2007-01-21 18:20:06 +00:00
|
|
|
TssEntry->HighWord.Bits.LimitHi = 0;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now clear the I/O Map */
|
2009-12-31 23:51:26 +00:00
|
|
|
ASSERT(IOPM_COUNT == 1);
|
|
|
|
RtlFillMemory(Tss->IoMaps[0].IoMap, IOPM_FULL_SIZE, 0xFF);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Initialize Interrupt Direction Maps */
|
|
|
|
p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);
|
2009-12-31 23:51:26 +00:00
|
|
|
RtlZeroMemory(p, IOPM_DIRECTION_MAP_SIZE);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Add DPMI support for interrupts */
|
|
|
|
p[0] = 4;
|
|
|
|
p[3] = 0x18;
|
|
|
|
p[4] = 0x18;
|
|
|
|
|
|
|
|
/* Initialize the default Interrupt Direction Map */
|
|
|
|
p = Tss->IntDirectionMap;
|
2009-12-31 23:51:26 +00:00
|
|
|
RtlZeroMemory(Tss->IntDirectionMap, IOPM_DIRECTION_MAP_SIZE);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* 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();
|
2010-01-19 18:27:24 +00:00
|
|
|
Tss->Esp0 = KiDoubleFaultStack;
|
|
|
|
Tss->Esp = KiDoubleFaultStack;
|
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot):
[NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile.
[NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way...
[NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro.
[NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing.
[NTOS]: Clean up some debug spew.
svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
|
|
|
Tss->Eip = PtrToUlong(KiTrap08);
|
2006-11-08 11:47:44 +00:00
|
|
|
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();
|
2010-01-19 18:27:24 +00:00
|
|
|
Tss->Esp0 = KiDoubleFaultStack;
|
|
|
|
Tss->Esp = KiDoubleFaultStack;
|
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot):
[NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile.
[NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way...
[NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro.
[NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing.
[NTOS]: Clean up some debug spew.
svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
|
|
|
Tss->Eip = PtrToUlong(KiTrap02);
|
2006-11-08 11:47:44 +00:00
|
|
|
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 */
|
2007-03-02 16:26:45 +00:00
|
|
|
__writecr3(__readcr3());
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState)
|
|
|
|
{
|
2009-10-04 16:53:15 +00:00
|
|
|
PKGDTENTRY TssEntry;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Restore the CR registers
|
|
|
|
//
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
__writecr0(ProcessorState->SpecialRegisters.Cr0);
|
|
|
|
Ke386SetCr2(ProcessorState->SpecialRegisters.Cr2);
|
|
|
|
__writecr3(ProcessorState->SpecialRegisters.Cr3);
|
2007-02-18 22:32:32 +00:00
|
|
|
if (KeFeatureBits & KF_CR4) __writecr4(ProcessorState->SpecialRegisters.Cr4);
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Restore the DR registers
|
|
|
|
//
|
2009-09-21 15:20:18 +00:00
|
|
|
__writedr(0, ProcessorState->SpecialRegisters.KernelDr0);
|
|
|
|
__writedr(1, ProcessorState->SpecialRegisters.KernelDr1);
|
|
|
|
__writedr(2, ProcessorState->SpecialRegisters.KernelDr2);
|
|
|
|
__writedr(3, ProcessorState->SpecialRegisters.KernelDr3);
|
|
|
|
__writedr(6, ProcessorState->SpecialRegisters.KernelDr6);
|
|
|
|
__writedr(7, ProcessorState->SpecialRegisters.KernelDr7);
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
|
|
|
|
//
|
2009-10-04 16:53:15 +00:00
|
|
|
// Restore GDT and IDT
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
//
|
2009-09-21 15:20:18 +00:00
|
|
|
Ke386SetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
|
|
|
|
__lidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
|
2009-10-04 16:53:15 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Clear the busy flag so we don't crash if we reload the same selector
|
|
|
|
//
|
|
|
|
TssEntry = (PKGDTENTRY)(ProcessorState->SpecialRegisters.Gdtr.Base +
|
|
|
|
ProcessorState->SpecialRegisters.Tr);
|
|
|
|
TssEntry->HighWord.Bytes.Flags1 &= ~0x2;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Restore TSS and LDT
|
|
|
|
//
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
Ke386SetTr(ProcessorState->SpecialRegisters.Tr);
|
|
|
|
Ke386SetLocalDescriptorTable(ProcessorState->SpecialRegisters.Ldtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
|
|
|
/* Save the CR registers */
|
|
|
|
ProcessorState->SpecialRegisters.Cr0 = __readcr0();
|
|
|
|
ProcessorState->SpecialRegisters.Cr2 = __readcr2();
|
|
|
|
ProcessorState->SpecialRegisters.Cr3 = __readcr3();
|
2007-02-18 22:32:32 +00:00
|
|
|
ProcessorState->SpecialRegisters.Cr4 = (KeFeatureBits & KF_CR4) ?
|
|
|
|
__readcr4() : 0;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Save the DR registers */
|
2009-09-21 15:20:18 +00:00
|
|
|
ProcessorState->SpecialRegisters.KernelDr0 = __readdr(0);
|
|
|
|
ProcessorState->SpecialRegisters.KernelDr1 = __readdr(1);
|
|
|
|
ProcessorState->SpecialRegisters.KernelDr2 = __readdr(2);
|
|
|
|
ProcessorState->SpecialRegisters.KernelDr3 = __readdr(3);
|
|
|
|
ProcessorState->SpecialRegisters.KernelDr6 = __readdr(6);
|
|
|
|
ProcessorState->SpecialRegisters.KernelDr7 = __readdr(7);
|
|
|
|
__writedr(7, 0);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Save GDT, IDT, LDT and TSS */
|
2009-09-21 15:20:18 +00:00
|
|
|
Ke386GetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
|
|
|
|
__sidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
|
2009-09-27 10:09:38 +00:00
|
|
|
ProcessorState->SpecialRegisters.Tr = Ke386GetTr();
|
|
|
|
ProcessorState->SpecialRegisters.Ldtr = Ke386GetLocalDescriptorTable();
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
2009-09-21 15:20:18 +00:00
|
|
|
WRMSR(0x174, KGDT_R0_CODE);
|
|
|
|
WRMSR(0x175, (ULONG_PTR)KeGetCurrentPrcb()->DpcStack);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Set LSTAR */
|
2009-09-21 15:20:18 +00:00
|
|
|
WRMSR(0x176, (ULONG_PTR)KiFastCallEntry);
|
2006-11-08 11:47:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-27 03:05:10 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiRestoreFastSyscallReturnState(VOID)
|
|
|
|
{
|
2006-11-08 11:47:44 +00:00
|
|
|
/* Check if the CPU Supports fast system call */
|
|
|
|
if (KeFeatureBits & KF_FAST_SYSCALL)
|
2010-01-27 03:05:10 +00:00
|
|
|
{
|
|
|
|
/* Check if it has been disabled */
|
|
|
|
if (!KiFastSystemCallDisable)
|
|
|
|
{
|
2010-03-17 13:12:46 +00:00
|
|
|
/* Do an IPI to enable it */
|
|
|
|
KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters, 0);
|
|
|
|
|
|
|
|
/* It's enabled, so use the proper exit stub */
|
|
|
|
KiFastCallExitHandler = KiSystemCallSysExitReturn;
|
2010-03-17 21:26:04 +00:00
|
|
|
DPRINT1("Support for SYSENTER detected.\n");
|
2010-01-27 03:05:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Disable fast system call */
|
|
|
|
KeFeatureBits &= ~KF_FAST_SYSCALL;
|
2010-03-17 13:12:46 +00:00
|
|
|
KiFastCallExitHandler = KiSystemCallTrapReturn;
|
2010-03-17 21:26:04 +00:00
|
|
|
DPRINT1("Support for SYSENTER disabled.\n");
|
2010-01-27 03:05:10 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-17 16:17:16 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Use the IRET handler */
|
|
|
|
KiFastCallExitHandler = KiSystemCallTrapReturn;
|
2010-03-17 21:26:04 +00:00
|
|
|
DPRINT1("No support for SYSENTER detected.\n");
|
2010-03-17 16:17:16 +00:00
|
|
|
}
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2007-01-18 06:23:14 +00:00
|
|
|
PKIDTENTRY IdtEntry;
|
|
|
|
|
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot):
[NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile.
[NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way...
[NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro.
[NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing.
[NTOS]: Clean up some debug spew.
svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
|
|
|
/* Get the IDT Entry for Interrupt 0x13 */
|
|
|
|
IdtEntry = &((PKIPCR)KeGetPcr())->IDT[0x13];
|
2007-01-18 06:23:14 +00:00
|
|
|
|
|
|
|
/* Set it up */
|
|
|
|
IdtEntry->Selector = KGDT_R0_CODE;
|
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot):
[NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile.
[NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way...
[NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro.
[NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing.
[NTOS]: Clean up some debug spew.
svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
|
|
|
IdtEntry->Offset = ((ULONG_PTR)KiTrap13 & 0xFFFF);
|
|
|
|
IdtEntry->ExtendedOffset = ((ULONG_PTR)KiTrap13 >> 16) & 0xFFFF;
|
2007-01-18 06:23:14 +00:00
|
|
|
((PKIDT_ACCESS)&IdtEntry->Access)->Dpl = 0;
|
|
|
|
((PKIDT_ACCESS)&IdtEntry->Access)->Present = 1;
|
|
|
|
((PKIDT_ACCESS)&IdtEntry->Access)->SegmentType = I386_INTERRUPT_GATE;
|
|
|
|
|
|
|
|
/* Enable XMMI exceptions */
|
|
|
|
__writecr4(__readcr4() | CR4_XMMEXCPT);
|
2006-11-08 11:47:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiI386PentiumLockErrataFixup(VOID)
|
|
|
|
{
|
2009-09-21 15:20:18 +00:00
|
|
|
KDESCRIPTOR IdtDescriptor;
|
2007-01-18 06:23:14 +00:00
|
|
|
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 */
|
2009-09-21 15:20:18 +00:00
|
|
|
__sidt(&IdtDescriptor.Limit);
|
2007-01-18 06:23:14 +00:00
|
|
|
RtlCopyMemory(NewIdt2,
|
|
|
|
(PVOID)IdtDescriptor.Base,
|
|
|
|
IdtDescriptor.Limit + 1);
|
|
|
|
IdtDescriptor.Base = (ULONG)NewIdt2;
|
|
|
|
|
|
|
|
/* Set the new IDT */
|
2009-09-21 15:20:18 +00:00
|
|
|
__lidt(&IdtDescriptor.Limit);
|
2007-01-18 06:23:14 +00:00
|
|
|
((PKIPCR)KeGetPcr())->IDT = NewIdt2;
|
|
|
|
|
|
|
|
/* Restore interrupts */
|
|
|
|
_enable();
|
|
|
|
|
|
|
|
/* Set the first 7 entries as read-only to produce a fault */
|
|
|
|
MmSetPageProtect(NULL, NewIdt, PAGE_READONLY);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
2009-11-02 17:45:51 +00:00
|
|
|
KeDisableInterrupts(VOID)
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
{
|
2009-09-21 15:20:18 +00:00
|
|
|
ULONG Flags;
|
2009-11-02 17:45:51 +00:00
|
|
|
BOOLEAN Return;
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
|
2009-11-02 17:45:51 +00:00
|
|
|
/* Get EFLAGS and check if the interrupt bit is set */
|
2009-09-21 15:20:18 +00:00
|
|
|
Flags = __readeflags();
|
2009-11-02 17:45:51 +00:00
|
|
|
Return = (Flags & EFLAGS_INTERRUPT_MASK) ? TRUE: FALSE;
|
|
|
|
|
|
|
|
/* Disable interrupts */
|
- Add EXCEPTION_RECORD64 and LIST_ENTRY64, KeTryToAcquireSpinLockAtDpcLevel, BREAKPOINT_COMMAND_STRING, Ke386SetCr2, Ke386SetDr3, Ke386SetDr6.
- Remove non-kernel routines from kdfuncs.h and remove deprecated routines from ke.h.
- Implement KiRestoreProcessorControlState, KeFreezeExecution, KeThawExecution, ExAcquireTimeRefreshLock, ExReleaseTimeRefreshLock.
- Rename ModuleLoadList to PsLoadedModuleList. Add PsNtosImageBase and set value in it.
- Add skeleton wdbgexts.h with what's needed until now, this is a PSDK header.
- Add kddll.h for KDCOM/1394/USB2.DLL prototypes.
- Add windbgkd.h with KD protocol definitions. Used to be an NT5 DDK header, but was removed, so this goes into include\reactos.
svn path=/branches/alex-kd-branch/; revision=25833
2007-02-18 07:21:03 +00:00
|
|
|
_disable();
|
2009-11-02 17:45:51 +00:00
|
|
|
return Return;
|
|
|
|
}
|
|
|
|
|
2007-09-26 16:41:35 +00:00
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
KeInvalidateAllCaches(VOID)
|
|
|
|
{
|
|
|
|
/* Only supported on Pentium Pro and higher */
|
|
|
|
if (KeI386CpuType < 6) return FALSE;
|
|
|
|
|
|
|
|
/* Invalidate all caches */
|
2007-11-25 22:33:50 +00:00
|
|
|
__wbinvd();
|
2007-09-26 16:41:35 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FASTCALL
|
|
|
|
KeZeroPages(IN PVOID Address,
|
|
|
|
IN ULONG Size)
|
|
|
|
{
|
|
|
|
/* Not using XMMI in this routine */
|
|
|
|
RtlZeroMemory(Address, Size);
|
|
|
|
}
|
|
|
|
|
2010-01-02 01:34:27 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN PKEXCEPTION_FRAME ExceptionFrame)
|
|
|
|
{
|
|
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Save full context
|
|
|
|
//
|
|
|
|
Prcb->ProcessorState.ContextFrame.ContextFlags = CONTEXT_FULL |
|
|
|
|
CONTEXT_DEBUG_REGISTERS;
|
|
|
|
KeTrapFrameToContext(TrapFrame, NULL, &Prcb->ProcessorState.ContextFrame);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Save control registers
|
|
|
|
//
|
|
|
|
KiSaveProcessorControlState(&Prcb->ProcessorState);
|
|
|
|
}
|
|
|
|
|
2010-01-20 04:05:08 +00:00
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
KiIsNpxPresent(VOID)
|
|
|
|
{
|
|
|
|
ULONG Cr0;
|
|
|
|
USHORT Magic;
|
|
|
|
|
|
|
|
/* Set magic */
|
|
|
|
Magic = 0xFFFF;
|
|
|
|
|
|
|
|
/* Read CR0 and mask out FPU flags */
|
|
|
|
Cr0 = __readcr0() & ~(CR0_MP | CR0_TS | CR0_EM | CR0_ET);
|
|
|
|
|
|
|
|
/* Store on FPU stack */
|
|
|
|
asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic));
|
|
|
|
|
|
|
|
/* Magic should now be cleared */
|
|
|
|
if (Magic & 0xFF)
|
|
|
|
{
|
|
|
|
/* You don't have an FPU -- enable emulation for now */
|
|
|
|
__writecr0(Cr0 | CR0_EM | CR0_TS);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* You have an FPU, enable it */
|
|
|
|
Cr0 |= CR0_ET;
|
|
|
|
|
|
|
|
/* Enable INT 16 on 486 and higher */
|
|
|
|
if (KeGetCurrentPrcb()->CpuType >= 3) Cr0 |= CR0_NE;
|
|
|
|
|
|
|
|
/* Set FPU state */
|
|
|
|
__writecr0(Cr0 | CR0_EM | CR0_TS);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
KiIsNpxErrataPresent(VOID)
|
|
|
|
{
|
|
|
|
BOOLEAN ErrataPresent;
|
|
|
|
ULONG Cr0;
|
|
|
|
volatile double Value1, Value2;
|
|
|
|
|
|
|
|
/* Disable interrupts */
|
|
|
|
_disable();
|
|
|
|
|
|
|
|
/* Read CR0 and remove FPU flags */
|
|
|
|
Cr0 = __readcr0();
|
|
|
|
__writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
|
|
|
|
|
|
|
|
/* Initialize FPU state */
|
|
|
|
asm volatile ("fninit");
|
|
|
|
|
|
|
|
/* Multiply the magic values and divide, we should get the result back */
|
|
|
|
Value1 = 4195835.0;
|
|
|
|
Value2 = 3145727.0;
|
|
|
|
ErrataPresent = (Value1 * Value2 / 3145727.0) != 4195835.0;
|
|
|
|
|
|
|
|
/* Restore CR0 */
|
|
|
|
__writecr0(Cr0);
|
|
|
|
|
|
|
|
/* Enable interrupts */
|
|
|
|
_enable();
|
|
|
|
|
|
|
|
/* Return if there's an errata */
|
|
|
|
return ErrataPresent;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTAPI
|
|
|
|
VOID
|
|
|
|
KiFlushNPXState(IN PFLOATING_SAVE_AREA SaveArea)
|
|
|
|
{
|
|
|
|
ULONG EFlags, Cr0;
|
|
|
|
PKTHREAD Thread, NpxThread;
|
|
|
|
PFX_SAVE_AREA FxSaveArea;
|
|
|
|
|
|
|
|
/* Save volatiles and disable interrupts */
|
|
|
|
EFlags = __readeflags();
|
|
|
|
_disable();
|
|
|
|
|
|
|
|
/* Save the PCR and get the current thread */
|
|
|
|
Thread = KeGetCurrentThread();
|
|
|
|
|
|
|
|
/* Check if we're already loaded */
|
|
|
|
if (Thread->NpxState != NPX_STATE_LOADED)
|
|
|
|
{
|
|
|
|
/* If there's nothing to load, quit */
|
|
|
|
if (!SaveArea) return;
|
|
|
|
|
|
|
|
/* Need FXSR support for this */
|
|
|
|
ASSERT(KeI386FxsrPresent == TRUE);
|
|
|
|
|
|
|
|
/* Check for sane CR0 */
|
|
|
|
Cr0 = __readcr0();
|
|
|
|
if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
|
|
|
|
{
|
|
|
|
/* Mask out FPU flags */
|
|
|
|
__writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the NPX thread and check its FPU state */
|
|
|
|
NpxThread = KeGetCurrentPrcb()->NpxThread;
|
|
|
|
if ((NpxThread) && (NpxThread->NpxState == NPX_STATE_LOADED))
|
|
|
|
{
|
|
|
|
/* Get the FX frame and store the state there */
|
|
|
|
FxSaveArea = KiGetThreadNpxArea(NpxThread);
|
|
|
|
Ke386FxSave(FxSaveArea);
|
|
|
|
|
|
|
|
/* NPX thread has lost its state */
|
|
|
|
NpxThread->NpxState = NPX_STATE_NOT_LOADED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now load NPX state from the NPX area */
|
|
|
|
FxSaveArea = KiGetThreadNpxArea(Thread);
|
|
|
|
Ke386FxStore(FxSaveArea);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Check for sane CR0 */
|
|
|
|
Cr0 = __readcr0();
|
|
|
|
if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
|
|
|
|
{
|
|
|
|
/* Mask out FPU flags */
|
|
|
|
__writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get FX frame */
|
|
|
|
FxSaveArea = KiGetThreadNpxArea(Thread);
|
|
|
|
Thread->NpxState = NPX_STATE_NOT_LOADED;
|
|
|
|
|
|
|
|
/* Save state if supported by CPU */
|
|
|
|
if (KeI386FxsrPresent) Ke386FxSave(FxSaveArea);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now save the FN state wherever it was requested */
|
|
|
|
if (SaveArea) Ke386FnSave(SaveArea);
|
|
|
|
|
|
|
|
/* Clear NPX thread */
|
|
|
|
KeGetCurrentPrcb()->NpxThread = NULL;
|
|
|
|
|
|
|
|
/* Add the CR0 from the NPX frame */
|
|
|
|
Cr0 |= NPX_STATE_NOT_LOADED;
|
|
|
|
Cr0 |= FxSaveArea->Cr0NpxState;
|
|
|
|
__writecr0(Cr0);
|
|
|
|
|
|
|
|
/* Restore interrupt state */
|
|
|
|
__writeeflags(EFlags);
|
|
|
|
}
|
|
|
|
|
2010-01-11 06:08:11 +00:00
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
2010-01-06 00:40:07 +00:00
|
|
|
|
2010-01-11 06:08:11 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
VOID
|
2010-01-06 00:40:07 +00:00
|
|
|
NTAPI
|
2010-01-11 06:08:11 +00:00
|
|
|
KiCoprocessorError(VOID)
|
2010-01-06 00:40:07 +00:00
|
|
|
{
|
2010-01-11 06:08:11 +00:00
|
|
|
PFX_SAVE_AREA NpxArea;
|
2010-01-06 00:40:07 +00:00
|
|
|
|
2010-01-11 06:08:11 +00:00
|
|
|
/* Get the FPU area */
|
|
|
|
NpxArea = KiGetThreadNpxArea(KeGetCurrentThread());
|
2010-01-06 00:40:07 +00:00
|
|
|
|
2010-01-11 06:08:11 +00:00
|
|
|
/* Set CR0_TS */
|
|
|
|
NpxArea->Cr0NpxState = CR0_TS;
|
|
|
|
__writecr0(__readcr0() | CR0_TS);
|
2010-01-06 00:40:07 +00:00
|
|
|
}
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)
|
|
|
|
{
|
|
|
|
PFNSAVE_FORMAT FpState;
|
2009-06-09 07:15:37 +00:00
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
2006-11-08 11:47:44 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-11-01 12:01:58 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiFlushTargetEntireTb(IN PKIPI_CONTEXT PacketContext,
|
|
|
|
IN PVOID Ignored1,
|
|
|
|
IN PVOID Ignored2,
|
|
|
|
IN PVOID Ignored3)
|
|
|
|
{
|
|
|
|
/* Signal this packet as done */
|
|
|
|
KiIpiSignalPacketDone(PacketContext);
|
|
|
|
|
|
|
|
/* Flush the TB for the Current CPU */
|
|
|
|
KeFlushCurrentTb();
|
|
|
|
}
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KeFlushEntireTb(IN BOOLEAN Invalid,
|
|
|
|
IN BOOLEAN AllProcessors)
|
|
|
|
{
|
|
|
|
KIRQL OldIrql;
|
2008-11-01 12:01:58 +00:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
KAFFINITY TargetAffinity;
|
|
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
|
|
#endif
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Raise the IRQL for the TB Flush */
|
|
|
|
OldIrql = KeRaiseIrqlToSynchLevel();
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
2008-11-01 12:01:58 +00:00
|
|
|
/* FIXME: Use KiTbFlushTimeStamp to synchronize TB flush */
|
|
|
|
|
|
|
|
/* Get the current processor affinity, and exclude ourselves */
|
|
|
|
TargetAffinity = KeActiveProcessors;
|
2008-11-01 19:22:43 +00:00
|
|
|
TargetAffinity &= ~Prcb->SetMember;
|
2008-11-01 12:01:58 +00:00
|
|
|
|
|
|
|
/* Make sure this is MP */
|
|
|
|
if (TargetAffinity)
|
|
|
|
{
|
|
|
|
/* Send an IPI TB flush to the other processors */
|
|
|
|
KiIpiSendPacket(TargetAffinity,
|
|
|
|
KiFlushTargetEntireTb,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
NULL);
|
|
|
|
}
|
2006-11-08 11:47:44 +00:00
|
|
|
#endif
|
|
|
|
|
2008-11-01 12:01:58 +00:00
|
|
|
/* Flush the TB for the Current CPU, and update the flush stamp */
|
2006-11-08 11:47:44 +00:00
|
|
|
KeFlushCurrentTb();
|
|
|
|
|
2008-11-01 12:01:58 +00:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
/* If this is MP, wait for the other processors to finish */
|
|
|
|
if (TargetAffinity)
|
|
|
|
{
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT(Prcb == (volatile PKPRCB)KeGetCurrentPrcb());
|
|
|
|
|
|
|
|
/* FIXME: TODO */
|
|
|
|
ASSERTMSG("Not yet implemented\n", FALSE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Update the flush stamp and return to original IRQL */
|
|
|
|
InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1);
|
2006-11-08 11:47:44 +00:00
|
|
|
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);
|
|
|
|
}
|