- Implement RtlPrefectMemoryNonTemporal. Patch by Patrick Baggett <baggett.patrick...
[reactos.git] / reactos / ntoskrnl / ke / i386 / kernel.c
index 9efc3aa..8f40320 100644 (file)
@@ -1,38 +1,16 @@
-/*
- *  ReactOS kernel
- *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
+/* $Id$
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/*
+ * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/ke/i386/kernel.c
  * PURPOSE:         Initializes the kernel
- * PROGRAMMER:      David Welch (welch@mcmail.com)
- * UPDATE HISTORY:
- *                  Created 22/05/98
+ *
+ * PROGRAMMERS:     David Welch (welch@mcmail.com)
  */
 
 /* INCLUDES *****************************************************************/
 
-#include <ddk/ntddk.h>
-#include <internal/ke.h>
-#include <internal/mm.h>
-#include <internal/ps.h>
-#include <internal/i386/fpu.h>
-
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
 
 
 ULONG KiPcrInitDone = 0;
 static ULONG PcrsAllocated = 0;
-static PHYSICAL_ADDRESS PcrPages[MAXIMUM_PROCESSORS];
-ULONG Ke386CpuidFlags, Ke386CpuidFlags2, Ke386CpuidExFlags;
+static ULONG Ke386CpuidFlags2, Ke386CpuidExFlags, Ke386CpuidExMisc;
+ULONG Ke386CacheAlignment;
+CHAR Ke386CpuidModel[49] = {0,};
+ULONG Ke386L1CacheSize;
+ULONG Ke386CacheGranularity = 0x40;      /* FIXME: Default to 64 bytes for RtlPrefetchMemoryNonTemporal(), need real size */
+BOOLEAN Ke386NoExecute = FALSE;
+BOOLEAN Ke386Pae = FALSE;
+BOOLEAN Ke386GlobalPagesEnabled = FALSE;
+ULONG KiFastSystemCallDisable = 1;
+extern PVOID Ki386InitialStackArray[MAXIMUM_PROCESSORS];
+extern ULONG IdleProcessorMask;
+
+static VOID INIT_FUNCTION Ki386GetCpuId(VOID);
 
-#define X86_EFLAGS_ID       (1 << 21)
+#if defined (ALLOC_PRAGMA)
+#pragma alloc_text(INIT, Ki386GetCpuId)
+#pragma alloc_text(INIT, KeCreateApplicationProcessorIdleThread)
+#pragma alloc_text(INIT, KePrepareForApplicationProcessorInit)
+#pragma alloc_text(INIT, KeInit1)
+#pragma alloc_text(INIT, KeInit2)
+#pragma alloc_text(INIT, Ki386SetProcessorFeatures)
+#endif
 
 /* FUNCTIONS *****************************************************************/
 
-VOID INIT_FUNCTION STATIC
+static VOID INIT_FUNCTION
 Ki386GetCpuId(VOID)
 {
-  ULONG OrigFlags, Flags, FinalFlags;
-  ULONG MaxCpuidLevel;
-
-  Ke386CpuidFlags = Ke386CpuidFlags2 =  Ke386CpuidExFlags = 0;
-
-  /* Try to toggle the id bit in eflags. */
-  __asm__ ("pushfl\n\t"
-          "popl %0\n\t"
-          : "=r" (OrigFlags));
-  Flags = OrigFlags ^ X86_EFLAGS_ID;
-  __asm__ ("pushl %1\n\t"
-          "popfl\n\t"
-          "pushfl\n\t"
-          "popl %0\n\t"
-          : "=r" (FinalFlags)
-          : "r" (Flags));
+   ULONG OrigFlags, Flags, FinalFlags;
+   ULONG MaxCpuidLevel;
+   ULONG Dummy, Eax, Ecx, Edx;
+   PKIPCR Pcr = (PKIPCR)KeGetCurrentKPCR();
+
+   Ke386CpuidFlags2 =  Ke386CpuidExFlags = 0;
+   Ke386CacheAlignment = 32;
+
+   /* Try to toggle the id bit in eflags. */
+   Ke386SaveFlags(OrigFlags);
+   Flags = OrigFlags ^ X86_EFLAGS_ID;
+   Ke386RestoreFlags(Flags);
+   Ke386SaveFlags(FinalFlags);
+
+   Pcr->PrcbData.LogicalProcessorsPerPhysicalProcessor = 1;
+   Pcr->PrcbData.InitialApicId = 0xff;
+
    if ((OrigFlags & X86_EFLAGS_ID) == (FinalFlags & X86_EFLAGS_ID))
-    {
+   {
       /* No cpuid supported. */
+      Pcr->PrcbData.CpuID = FALSE;
+      Pcr->PrcbData.CpuType = 3;
       return;
-    }
-  
-  /* Get maximum cpuid level supported. */
-  __asm__("cpuid\n\t"
-         : "=a" (MaxCpuidLevel)
-         : "a" (0x00000000)
-         : "ebx", "ecx", "edx");       
-  if (MaxCpuidLevel > 0)
-    {
+   }
+   Pcr->PrcbData.CpuID = TRUE;
+
+   /* Get the vendor name and the maximum cpuid level supported. */
+   Ki386Cpuid(0, &MaxCpuidLevel, (PULONG)&Pcr->PrcbData.VendorString[0], (PULONG)&Pcr->PrcbData.VendorString[8], (PULONG)&Pcr->PrcbData.VendorString[4]);
+   if (MaxCpuidLevel > 0)
+   {
       /* Get the feature flags. */
-      __asm__("cpuid\n\t"
-         : "=d" (Ke386CpuidFlags), "=c" (Ke386CpuidFlags2)
-         : "a" (0x00000001)
-         : "ebx");       
-    }
+      Ki386Cpuid(1, &Eax, &Ke386CpuidExMisc, &Ke386CpuidFlags2, &Pcr->PrcbData.FeatureBits);
+
+      DPRINT ("Model:  %x\n", (Eax & 0xf00) == 0xf00 ? ((Eax >> 4) & 0xf) | ((Eax >> 12) & 0xf0) : (Eax >> 4) & 0xf);
+      DPRINT ("Family: %x\n", (Eax & 0xf00) == 0xf00 ? ((Eax >> 8) & 0xf) + ((Eax >> 20) & 0xff) : (Eax >> 8) & 0xf);
+
+      /* Get the cache alignment, if it is available */
+      if (Pcr->PrcbData.FeatureBits & (1<<19))
+      {
+         Ke386CacheAlignment = ((Ke386CpuidExMisc >> 8) & 0xff) * 8;
+      }
+      Pcr->PrcbData.CpuType = (Eax >> 8) & 0xf;
+      Pcr->PrcbData.CpuStep = (Eax & 0xf) | ((Eax << 4) & 0xf00);
 
-  /* Get the maximum extended cpuid level supported. */
-  __asm__("cpuid\n\t"
-         : "=a" (MaxCpuidLevel)
-         : "a" (0x80000000)
-         : "ebx", "ecx", "edx");       
-  if (MaxCpuidLevel > 0)
-    {
+      Pcr->PrcbData.InitialApicId = (Ke386CpuidExMisc >> 24) & 0xff;
+
+      /* detect Hyper-Threading on Pentium 4 CPUs or later */
+      if ((Pcr->PrcbData.CpuType == 0xf || (Eax & 0x0f00000)) &&
+          !strncmp(Pcr->PrcbData.VendorString, "GenuineIntel", 12) &&
+          Pcr->PrcbData.FeatureBits & X86_FEATURE_HT)
+      {
+        Pcr->PrcbData.LogicalProcessorsPerPhysicalProcessor = (Ke386CpuidExMisc >> 16) & 0xff;
+      }
+   }
+   else
+   {
+      Pcr->PrcbData.CpuType = 4;
+   }
+
+   /* Get the maximum extended cpuid level supported. */
+   Ki386Cpuid(0x80000000, &MaxCpuidLevel, &Dummy, &Dummy, &Dummy);
+   if (MaxCpuidLevel > 0)
+   {
       /* Get the extended feature flags. */
-      __asm__("cpuid\n\t"
-         : "=d" (Ke386CpuidExFlags)
-         : "a" (0x80000001)
-         : "ebx", "ecx");       
-    }
+      Ki386Cpuid(0x80000001, &Dummy, &Dummy, &Dummy, &Ke386CpuidExFlags);
+   }
+
+   /* Get the model name. */
+   if (MaxCpuidLevel >= 0x80000004)
+   {
+      PULONG v = (PULONG)Ke386CpuidModel;
+      Ki386Cpuid(0x80000002, v, v + 1, v + 2, v + 3);
+      Ki386Cpuid(0x80000003, v + 4, v + 5, v + 6, v + 7);
+      Ki386Cpuid(0x80000004, v + 8, v + 9, v + 10, v + 11);
+   }
+
+   /* Get the L1 cache size */
+   if (MaxCpuidLevel >= 0x80000005)
+   {
+      Ki386Cpuid(0x80000005, &Dummy, &Dummy, &Ecx, &Edx);
+      Ke386L1CacheSize = (Ecx >> 24)+(Edx >> 24);
+      if ((Ecx & 0xff) > 0)
+      {
+         Ke386CacheAlignment = Ecx & 0xff;
+      }
+   }
+
+   /* Get the L2 cache size */
+   if (MaxCpuidLevel >= 0x80000006)
+   {
+      Ki386Cpuid(0x80000006, &Dummy, &Dummy, &Ecx, &Dummy);
+      Pcr->L2CacheSize = Ecx >> 16;
+   }
 }
 
-VOID INIT_FUNCTION
-KePrepareForApplicationProcessorInit(ULONG Id)
+VOID
+KeApplicationProcessorInitDispatcher(VOID)
 {
-  MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &PcrPages[Id]);
-  KiGdtPrepareForApplicationProcessorInit(Id);
+   KIRQL oldIrql;
+   oldIrql = KeAcquireDispatcherDatabaseLock();
+   IdleProcessorMask |= (1 << KeGetCurrentProcessorNumber());
+   KeReleaseDispatcherDatabaseLock(oldIrql);
 }
 
 VOID
-KeApplicationProcessorInit(VOID)
+INIT_FUNCTION
+KeCreateApplicationProcessorIdleThread(ULONG Id)
 {
-  PKPCR KPCR;
-  ULONG Offset;
+  PETHREAD IdleThread;
+  PKPRCB Prcb = ((PKPCR)((ULONG_PTR)KPCR_BASE + Id * PAGE_SIZE))->Prcb;
+
+  PsInitializeIdleOrFirstThread(PsIdleProcess,
+                    &IdleThread,
+                    NULL,
+                    KernelMode,
+             FALSE);
+  IdleThread->Tcb.State = Running;
+  IdleThread->Tcb.FreezeCount = 0;
+  IdleThread->Tcb.Affinity = 1 << Id;
+  IdleThread->Tcb.UserAffinity = 1 << Id;
+  IdleThread->Tcb.Priority = LOW_PRIORITY;
+  IdleThread->Tcb.BasePriority = LOW_PRIORITY;
+  Prcb->IdleThread = &IdleThread->Tcb;
+  Prcb->CurrentThread = &IdleThread->Tcb;
+
+  Ki386InitialStackArray[Id] = (PVOID)IdleThread->Tcb.StackLimit;
 
+  DPRINT("IdleThread for Processor %d has PID %d\n",
+          Id, IdleThread->Cid.UniqueThread);
+}
+
+VOID
+INIT_FUNCTION
+NTAPI
+KePrepareForApplicationProcessorInit(ULONG Id)
+{
+  PFN_TYPE PrcPfn;
+  PKIPCR Pcr;
+  PKIPCR BootPcr;
+
+  DPRINT("KePrepareForApplicationProcessorInit(Id %d)\n", Id);
+
+  BootPcr = (PKIPCR)KPCR_BASE;
+  Pcr = (PKIPCR)((ULONG_PTR)KPCR_BASE + Id * PAGE_SIZE);
+
+  MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &PrcPfn);
+  MmCreateVirtualMappingForKernel((PVOID)Pcr,
+                                 PAGE_READWRITE,
+                                 &PrcPfn,
+                                 1);
   /*
    * Create a PCR for this processor
    */
-  Offset = InterlockedIncrement((LONG *)&PcrsAllocated) - 1;
-  KPCR = (PKPCR)(KPCR_BASE + (Offset * PAGE_SIZE));
-  MmCreateVirtualMappingForKernel((PVOID)KPCR,
-                                 PAGE_READWRITE,
-                                 PcrPages[Offset]);
-  memset(KPCR, 0, PAGE_SIZE);
-  KPCR->ProcessorNumber = (UCHAR)Offset;
-  KPCR->Self = KPCR;
-  KPCR->Irql = HIGH_LEVEL;
+  memset(Pcr, 0, PAGE_SIZE);
+  Pcr->Number = Id;
+  Pcr->SetMember = 1 << Id;
+  Pcr->NtTib.Self = &Pcr->NtTib;
+  Pcr->Self = (PKPCR)Pcr;
+  Pcr->Prcb = &Pcr->PrcbData;
+  Pcr->Irql = SYNCH_LEVEL;
+
+  Pcr->PrcbData.SetMember = 1 << Id;
+  Pcr->PrcbData.MHz = BootPcr->PrcbData.MHz;
+  Pcr->StallScaleFactor = BootPcr->StallScaleFactor;
 
   /* Mark the end of the exception handler list */
-  KPCR->Tib.ExceptionList = (PVOID)-1;
+  Pcr->NtTib.ExceptionList = (PVOID)-1;
+
+  KiGdtPrepareForApplicationProcessorInit(Id);
+
+  KeActiveProcessors |= 1 << Id;
+}
+
+VOID
+NTAPI
+KeApplicationProcessorInit(VOID)
+{
+  ULONG Offset;
+  PKIPCR Pcr;
+
+  DPRINT("KeApplicationProcessorInit()\n");
+
+  if (Ke386GlobalPagesEnabled)
+  {
+     /* Enable global pages */
+     Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
+  }
+
+
+  Offset = InterlockedIncrementUL(&PcrsAllocated) - 1;
+  Pcr = (PKIPCR)((ULONG_PTR)KPCR_BASE + Offset * PAGE_SIZE);
 
   /*
    * Initialize the GDT
    */
-  KiInitializeGdt(KPCR);
-  
+  KiInitializeGdt((PKPCR)Pcr);
+
+  /* Get processor information. */
+  Ki386GetCpuId();
+
+  /* Check FPU/MMX/SSE support. */
+  KiCheckFPU();
+
+  KeInitDpc(Pcr->Prcb);
+
+  if (Pcr->PrcbData.FeatureBits & X86_FEATURE_SYSCALL)
+  {
+     extern void KiFastCallEntry(void);
+
+     /* CS Selector of the target segment. */
+     Ke386Wrmsr(0x174, KGDT_R0_CODE, 0);
+     /* Target ESP. */
+     Ke386Wrmsr(0x175, 0, 0);
+     /* Target EIP. */
+     Ke386Wrmsr(0x176, (ULONG_PTR)KiFastCallEntry, 0);
+  }
+
   /*
    * It is now safe to process interrupts
    */
@@ -154,50 +282,296 @@ KeApplicationProcessorInit(VOID)
   Ke386EnableInterrupts();
 }
 
-VOID INIT_FUNCTION
-KeInit1(VOID)
+VOID
+INIT_FUNCTION
+NTAPI
+KeInit1(PCHAR CommandLine, PULONG LastKernelAddress)
 {
-   PKPCR KPCR;
+   PKIPCR KPCR;
+   BOOLEAN Pae = FALSE;
+   BOOLEAN NoExecute = FALSE;
+   PCHAR p1, p2;
    extern USHORT KiBootGdt[];
    extern KTSS KiBootTss;
 
-   KiCheckFPU();
-   
-   KiInitializeGdt (NULL);
-   Ki386BootInitializeTSS();
-   KeInitExceptions ();
-   KeInitInterrupts ();
-
-   /* 
+   /*
     * Initialize the initial PCR region. We can't allocate a page
     * with MmAllocPage() here because MmInit1() has not yet been
-    * called, so we use a predefined page in low memory 
+    * called, so we use a predefined page in low memory
     */
-   KPCR = (PKPCR)KPCR_BASE;
+
+   KPCR = (PKIPCR)KPCR_BASE;
    memset(KPCR, 0, PAGE_SIZE);
-   KPCR->Self = (PKPCR)KPCR_BASE;
-   KPCR->Irql = HIGH_LEVEL;
-   KPCR->GDT = (PUSHORT)&KiBootGdt;
-   KPCR->IDT = (PUSHORT)&KiIdt;
+   KPCR->Self = (PKPCR)KPCR;
+   KPCR->Prcb = &KPCR->PrcbData;
+   KPCR->Irql = SYNCH_LEVEL;
+   KPCR->NtTib.Self = &KPCR->NtTib;
+   KPCR->GDT = KiBootGdt;
+   KPCR->IDT = (PUSHORT)KiIdt;
    KPCR->TSS = &KiBootTss;
-   KPCR->ProcessorNumber = 0;
+   KPCR->Number = 0;
+   KPCR->SetMember = 1 << 0;
+   KeActiveProcessors = 1 << 0;
+   KPCR->PrcbData.SetMember = 1 << 0;
    KiPcrInitDone = 1;
    PcrsAllocated++;
 
-   /* Mark the end of the exception handler list */
-   KPCR->Tib.ExceptionList = (PVOID)-1;
-
+   KiInitializeGdt (NULL);
+   Ki386BootInitializeTSS();
    Ki386InitializeLdt();
-   
+
    /* Get processor information. */
    Ki386GetCpuId();
+
+   /* Check FPU/MMX/SSE support. */
+   KiCheckFPU();
+
+   /* Mark the end of the exception handler list */
+   KPCR->NtTib.ExceptionList = (PVOID)-1;
+
+   KeInitDpc(KPCR->Prcb);
+
+   KeInitExceptions ();
+   KeInitInterrupts ();
+
+   KeActiveProcessors |= 1 << 0;
+
+
+   if (KPCR->PrcbData.FeatureBits & X86_FEATURE_PGE)
+   {
+      ULONG Flags;
+      /* Enable global pages */
+      Ke386GlobalPagesEnabled = TRUE;
+      Ke386SaveFlags(Flags);
+      Ke386DisableInterrupts();
+      Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
+      Ke386RestoreFlags(Flags);
+   }
+
+   /* Search for pae and noexecute */
+   p1 = (PCHAR)KeLoaderBlock.CommandLine;
+   while(*p1 && (p2 = strchr(p1, '/')))
+   {
+      p2++;
+      if (!_strnicmp(p2, "PAE", 3))
+      {
+        if (p2[3] == ' ' || p2[3] == 0)
+        {
+           p2 += 3;
+           Pae = TRUE;
+        }
+      }
+      else if (!_strnicmp(p2, "NOEXECUTE", 9))
+      {
+         if (p2[9] == ' ' || p2[9] == '=' || p2[9] == 0)
+        {
+           p2 += 9;
+           NoExecute = TRUE;
+        }
+      }
+      p1 = p2;
+   }
+#if 0
+   /*
+    * FIXME:
+    *   Make the detection of the noexecute feature more portable.
+    */
+   if(KPCR->PrcbData.CpuType == 0xf &&
+      RtlCompareMemory("AuthenticAMD", KPCR->PrcbData.VendorString, 12) == 12)
+   {
+      if (NoExecute)
+      {
+         ULONG Flags, l, h;
+         Ke386SaveFlags(Flags);
+         Ke386DisableInterrupts();
+
+        Ke386Rdmsr(0xc0000080, l, h);
+        l |= (1 << 11);
+        Ke386Wrmsr(0xc0000080, l, h);
+        Ke386NoExecute = TRUE;
+         Ke386RestoreFlags(Flags);
+      }
+   }
+   else
+   {
+      NoExecute=FALSE;
+   }
+#endif
+
+   Ke386Pae = Ke386GetCr4() & X86_CR4_PAE ? TRUE : FALSE;
+#if 0
+   /* Enable PAE mode */
+   if ((Pae && (KPCR->PrcbData.FeatureBits & X86_FEATURE_PAE)) || NoExecute)
+   {
+      MiEnablePAE((PVOID*)LastKernelAddress);
+      Ke386PaeEnabled = TRUE;
+   }
+#endif
+   if (KPCR->PrcbData.FeatureBits & X86_FEATURE_SYSCALL)
+   {
+      extern void KiFastCallEntry(void);
+
+      /* CS Selector of the target segment. */
+      Ke386Wrmsr(0x174, KGDT_R0_CODE, 0);
+      /* Target ESP. */
+      Ke386Wrmsr(0x175, 0, 0);
+      /* Target EIP. */
+      Ke386Wrmsr(0x176, (ULONG_PTR)KiFastCallEntry, 0);
+   }
 }
 
-VOID INIT_FUNCTION
+VOID
+INIT_FUNCTION
+NTAPI
 KeInit2(VOID)
 {
-   KeInitDpc();
-   KeInitializeBugCheck();
+   PKIPCR Pcr = (PKIPCR)KeGetCurrentKPCR();
+
+   KiInitializeBugCheck();
    KeInitializeDispatcher();
-   KeInitializeTimerImpl();
+   KiInitializeSystemClock();
+
+   if (Pcr->PrcbData.FeatureBits & X86_FEATURE_PAE)
+   {
+      DPRINT("CPU supports PAE mode\n");
+      if (Ke386Pae)
+      {
+         DPRINT("CPU runs in PAE mode\n");
+         if (Ke386NoExecute)
+         {
+            DPRINT("NoExecute is enabled\n");
+         }
+      }
+      else
+      {
+         DPRINT("CPU doesn't run in PAE mode\n");
+      }
+   }
+   if ((Pcr->PrcbData.FeatureBits & (X86_FEATURE_FXSR | X86_FEATURE_MMX | X86_FEATURE_SSE | X86_FEATURE_SSE2)) ||
+       (Ke386CpuidFlags2 & X86_EXT_FEATURE_SSE3))
+      {
+         DPRINT("CPU supports" "%s%s%s%s%s" ".\n",
+                ((Pcr->PrcbData.FeatureBits & X86_FEATURE_FXSR) ? " FXSR" : ""),
+                ((Pcr->PrcbData.FeatureBits & X86_FEATURE_MMX) ? " MMX" : ""),
+                ((Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE) ? " SSE" : ""),
+                ((Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE2) ? " SSE2" : ""),
+                ((Ke386CpuidFlags2 & X86_EXT_FEATURE_SSE3) ? " SSE3" : ""));
+      }
+   if (Ke386GetCr4() & X86_CR4_OSFXSR)
+      {
+         DPRINT("SSE enabled.\n");
+      }
+   if (Ke386GetCr4() & X86_CR4_OSXMMEXCPT)
+      {
+         DPRINT("Unmasked SIMD exceptions enabled.\n");
+      }
+   if (Pcr->PrcbData.VendorString[0])
+   {
+      DPRINT("CPU Vendor: %s\n", Pcr->PrcbData.VendorString);
+   }
+   if (Ke386CpuidModel[0])
+   {
+      DPRINT("CPU Model:  %s\n", Ke386CpuidModel);
+   }
+
+   DPRINT("Ke386CacheAlignment: %d\n", Ke386CacheAlignment);
+   if (Ke386L1CacheSize)
+   {
+
+      DPRINT("Ke386L1CacheSize: %dkB\n", Ke386L1CacheSize);
+   }
+   if (Pcr->L2CacheSize)
+   {
+      DPRINT("Ke386L2CacheSize: %dkB\n", Pcr->L2CacheSize);
+   }
+}
+
+VOID INIT_FUNCTION
+Ki386SetProcessorFeatures(VOID)
+{
+   PKIPCR Pcr = (PKIPCR)KeGetCurrentKPCR();
+   OBJECT_ATTRIBUTES ObjectAttributes;
+   UNICODE_STRING KeyName =
+   RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Kernel");
+   UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"FastSystemCallDisable");
+   HANDLE KeyHandle;
+   ULONG ResultLength;
+   KEY_VALUE_PARTIAL_INFORMATION ValueData;
+   NTSTATUS Status;
+   ULONG FastSystemCallDisable = 0;
+   
+   SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
+   SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = FALSE;
+   SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
+      (Pcr->PrcbData.FeatureBits & X86_FEATURE_CX8);
+   SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
+      (Pcr->PrcbData.FeatureBits & X86_FEATURE_MMX);
+   SharedUserData->ProcessorFeatures[PF_PPC_MOVEMEM_64BIT_OK] = FALSE;
+   SharedUserData->ProcessorFeatures[PF_ALPHA_BYTE_INSTRUCTIONS] = FALSE;
+   SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
+      (Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE);
+   SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
+      (Ke386CpuidExFlags & X86_EXT_FEATURE_3DNOW);
+   SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
+      (Pcr->PrcbData.FeatureBits & X86_FEATURE_TSC);
+   SharedUserData->ProcessorFeatures[PF_PAE_ENABLED] = Ke386Pae;
+   SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
+      (Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE2);
+
+   /* Does the CPU Support 'prefetchnta' (SSE)  */
+   if(Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE)
+   {
+       /* Replace the ret by a nop */
+       *(PCHAR)RtlPrefetchMemoryNonTemporal = 0x90;
+   }
+
+   /* Does the CPU Support Fast System Call? */
+   if (Pcr->PrcbData.FeatureBits & X86_FEATURE_SYSCALL) {
+
+        /* FIXME: Check for Family == 6, Model < 3 and Stepping < 3 and disable */
+
+        /* Make sure it's not disabled in registry */
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   &KeyName,
+                                   OBJ_CASE_INSENSITIVE,
+                                   NULL,
+                                   NULL);
+        Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
+
+        if (NT_SUCCESS(Status)) {
+
+            /* Read the Value then Close the Key */
+            Status = NtQueryValueKey(KeyHandle,
+                                     &ValueName,
+                                     KeyValuePartialInformation,
+                                     &ValueData,
+                                     sizeof(ValueData),
+                                     &ResultLength);
+            RtlMoveMemory(&FastSystemCallDisable, ValueData.Data, sizeof(ULONG));
+
+            NtClose(KeyHandle);
+        }
+
+    } else {
+
+        /* Disable SYSENTER/SYSEXIT, because the CPU doesn't support it */
+        FastSystemCallDisable = 1;
+
+    }
+
+    if (FastSystemCallDisable) {
+        /* Use INT2E */
+        const unsigned char Entry[7] = {0x8D, 0x54, 0x24, 0x08,     /* lea    0x8(%esp),%edx    */
+                                        0xCD, 0x2E,                 /* int    0x2e              */
+                                        0xC3};                      /* ret                      */
+        memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));
+    } else {
+        /* Use SYSENTER */
+        const unsigned char Entry[5] = {0x8B, 0xD4,                 /* movl    %esp,%edx        */ 
+                                        0x0F, 0x34,                 /* sysenter                 */
+                                        0xC3};                      /* ret                      */    
+        memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));
+        /* Enable SYSENTER/SYSEXIT */
+        KiFastSystemCallDisable = 0;
+    }
 }