[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ke / i386 / cpu.c
index 775ac85..4b56a2b 100644 (file)
@@ -27,7 +27,7 @@ ULONG KeProcessorArchitecture;
 ULONG KeProcessorLevel;
 ULONG KeProcessorRevision;
 ULONG KeFeatureBits;
-ULONG KiFastSystemCallDisable = 1;
+ULONG KiFastSystemCallDisable;
 ULONG KeI386NpxPresent = 0;
 ULONG KiMXCsrMask = 0;
 ULONG MxcsrFeatureMask = 0;
@@ -40,11 +40,21 @@ ULONG KeLargestCacheLine = 0x40;
 ULONG KeDcacheFlushCount = 0;
 ULONG KeIcacheFlushCount = 0;
 ULONG KiDmaIoCoherency = 0;
+ULONG KePrefetchNTAGranularity = 32;
 CHAR KeNumberProcessors;
 KAFFINITY KeActiveProcessors = 1;
 BOOLEAN KiI386PentiumLockErrataPresent;
 BOOLEAN KiSMTProcessorsPresent;
 
+/* 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;
+
 /* Flush data */
 volatile LONG KiTbFlushTimeStamp;
 
@@ -95,10 +105,28 @@ RDMSR(IN ULONG Register)
     return __readmsr(Register);
 }
 
+/* NSC/Cyrix CPU configuration register index */
+#define CX86_CCR1 0xc1
+
+/* NSC/Cyrix CPU indexed register access macros */
+static __inline
+ULONG
+getCx86(UCHAR reg)
+{
+    WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x22, reg);
+    return READ_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x23);
+}
+
+#define setCx86(reg, data) do { \
+   WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x22,(reg)); \
+   WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x23,(data)); \
+} while (0)
+
 /* FUNCTIONS *****************************************************************/
 
 VOID
 NTAPI
+INIT_FUNCTION
 KiSetProcessorType(VOID)
 {
     ULONG EFlags, NewEFlags;
@@ -165,6 +193,7 @@ KiSetProcessorType(VOID)
 
 ULONG
 NTAPI
+INIT_FUNCTION
 KiGetCpuVendor(VOID)
 {
     PKPRCB Prcb = KeGetCurrentPrcb();
@@ -191,30 +220,30 @@ KiGetCpuVendor(VOID)
     Prcb->VendorString[sizeof(Prcb->VendorString) - sizeof(CHAR)] = ANSI_NULL;
 
     /* Now check the CPU Type */
-    if (!strcmp((PCHAR)Prcb->VendorString, CmpIntelID))
+    if (!strcmp(Prcb->VendorString, CmpIntelID))
     {
         return CPU_INTEL;
     }
-    else if (!strcmp((PCHAR)Prcb->VendorString, CmpAmdID))
+    else if (!strcmp(Prcb->VendorString, CmpAmdID))
     {
         return CPU_AMD;
     }
-    else if (!strcmp((PCHAR)Prcb->VendorString, CmpCyrixID))
+    else if (!strcmp(Prcb->VendorString, CmpCyrixID))
     {
         DPRINT1("Cyrix CPU support not fully tested!\n");
         return CPU_CYRIX;
     }
-    else if (!strcmp((PCHAR)Prcb->VendorString, CmpTransmetaID))
+    else if (!strcmp(Prcb->VendorString, CmpTransmetaID))
     {
         DPRINT1("Transmeta CPU support not fully tested!\n");
         return CPU_TRANSMETA;
     }
-    else if (!strcmp((PCHAR)Prcb->VendorString, CmpCentaurID))
+    else if (!strcmp(Prcb->VendorString, CmpCentaurID))
     {
         DPRINT1("Centaur CPU support not fully tested!\n");
         return CPU_CENTAUR;
     }
-    else if (!strcmp((PCHAR)Prcb->VendorString, CmpRiseID))
+    else if (!strcmp(Prcb->VendorString, CmpRiseID))
     {
         DPRINT1("Rise CPU support not fully tested!\n");
         return CPU_RISE;
@@ -226,12 +255,13 @@ KiGetCpuVendor(VOID)
 
 ULONG
 NTAPI
+INIT_FUNCTION
 KiGetFeatureBits(VOID)
 {
     PKPRCB Prcb = KeGetCurrentPrcb();
     ULONG Vendor;
     ULONG FeatureBits = KF_WORKING_PTE;
-    ULONG Reg[4], Dummy;
+    ULONG Reg[4], Dummy, Ccr1;
     BOOLEAN ExtendedCPUID = TRUE;
     ULONG CpuFeatures = 0;
 
@@ -273,10 +303,16 @@ KiGetFeatureBits(VOID)
                 /* Remove support for correct PTE support. */
                 FeatureBits &= ~KF_WORKING_PTE;
             }
+            
+            /* Virtualbox claims to have no SYSENTER support,
+             * which is false for processors >= Pentium Pro */
+            if(Prcb->CpuType >= 6)
+            {
+                Reg[3] |= 0x800;
+            }
 
             /* Check if the CPU is too old to support SYSENTER */
-            if ((Prcb->CpuType < 6) ||
-                ((Prcb->CpuType == 6) && (Prcb->CpuStep < 0x0303)))
+            if ((Reg[0] & 0x0FFF3FFF) < 0x00000633)
             {
                 /* Disable it */
                 Reg[3] &= ~0x800;
@@ -342,7 +378,22 @@ KiGetFeatureBits(VOID)
         /* Cyrix CPUs */
         case CPU_CYRIX:
 
-            /* FIXME: CMPXCGH8B */
+            /* Workaround the "COMA" bug on 6x family of Cyrix CPUs */
+            if (Prcb->CpuType == 6 &&
+                Prcb->CpuStep <= 1)
+            {
+                /* Get CCR1 value */
+                Ccr1 = getCx86(CX86_CCR1);
+
+                /* Enable the NO_LOCK bit */
+                Ccr1 |= 0x10;
+
+                /* Set the new CCR1 value */
+                setCx86(CX86_CCR1, Ccr1);
+            }
+
+            /* Set the current features */
+            CpuFeatures = Reg[3];
 
             break;
 
@@ -429,6 +480,32 @@ KiGetFeatureBits(VOID)
             }
         }
     }
+    
+    DPRINT1("Supported CPU features :\n");
+#define print_supported(kf_value) \
+    if(FeatureBits & kf_value) DPRINT1("\t" #kf_value "\n")
+    print_supported(KF_V86_VIS);
+    print_supported(KF_RDTSC);
+    print_supported(KF_CR4);
+    print_supported(KF_CMOV);
+    print_supported(KF_GLOBAL_PAGE);
+    print_supported(KF_LARGE_PAGE);
+    print_supported(KF_MTRR);
+    print_supported(KF_CMPXCHG8B);
+    print_supported(KF_MMX);
+    print_supported(KF_WORKING_PTE);
+    print_supported(KF_PAT);
+    print_supported(KF_FXSR);
+    print_supported(KF_FAST_SYSCALL);
+    print_supported(KF_XMMI);
+    print_supported(KF_3DNOW);
+    print_supported(KF_AMDK6MTRR);
+    print_supported(KF_XMMI64);
+    print_supported(KF_DTS);
+    print_supported(KF_NX_BIT);
+    print_supported(KF_NX_DISABLED);
+    print_supported(KF_NX_ENABLED);
+#undef print_supported
 
     /* Return the Feature Bits */
     return FeatureBits;
@@ -436,6 +513,7 @@ KiGetFeatureBits(VOID)
 
 VOID
 NTAPI
+INIT_FUNCTION
 KiGetCacheInformation(VOID)
 {
     PKIPCR Pcr = (PKIPCR)KeGetPcr();
@@ -444,6 +522,7 @@ KiGetCacheInformation(VOID)
     ULONG CacheRequests = 0, i;
     ULONG CurrentRegister;
     UCHAR RegisterByte;
+    ULONG Size, Associativity = 0, CacheLine = 64, CurrentSize = 0;
     BOOLEAN FirstPass = TRUE;
 
     /* Set default L2 size */
@@ -508,17 +587,144 @@ KiGetCacheInformation(VOID)
                              * (32MB), or from 0x80 to 0x89 (same size but
                              * 8-way associative.
                              */
-                            if (((RegisterByte > 0x40) &&
-                                 (RegisterByte <= 0x49)) ||
-                                ((RegisterByte > 0x80) &&
-                                (RegisterByte <= 0x89)))
+                            if (((RegisterByte > 0x40) && (RegisterByte <= 0x47)) ||
+                                ((RegisterByte > 0x78) && (RegisterByte <= 0x7C)) ||
+                                ((RegisterByte > 0x80) && (RegisterByte <= 0x85)))
                             {
+                                /* Compute associativity */
+                                Associativity = 4;
+                                if (RegisterByte >= 0x79) Associativity = 8;
+                                
                                 /* Mask out only the first nibble */
-                                RegisterByte &= 0x0F;
-
-                                /* Set the L2 Cache Size */
-                                Pcr->SecondLevelCacheSize = 0x10000 <<
-                                                            RegisterByte;
+                                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;
+                                }
                             }
                         }
                     }
@@ -528,15 +734,65 @@ KiGetCacheInformation(VOID)
 
         case CPU_AMD:
 
-            /* Check if we support CPUID 0x80000006 */
-            CPUID(0x80000000, &Data[0], &Dummy, &Dummy, &Dummy);
-            if (Data[0] >= 6)
+            /* Check if we support CPUID 0x80000005 */
+            CPUID(0x80000000, &Data[0], &Data[1], &Data[2], &Data[3]);
+            if (Data[0] >= 0x80000006)
             {
-                /* Get 2nd level cache and tlb size */
-                CPUID(0x80000006, &Dummy, &Dummy, &Data[2], &Dummy);
+                /* 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;
+                    }
 
-                /* Set the L2 Cache Size */
-                Pcr->SecondLevelCacheSize = (Data[2] & 0xFFFF0000) >> 6;
+                    /* Set the L2 Cache Size and associativity */
+                    Pcr->SecondLevelCacheSize = Size;
+                    Pcr->SecondLevelCacheAssociativity = Associativity;
+                }
             }
             break;
 
@@ -548,10 +804,19 @@ KiGetCacheInformation(VOID)
             /* FIXME */
             break;
     }
+    
+    /* 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);
 }
 
 VOID
 NTAPI
+INIT_FUNCTION
 KiSetCR0Bits(VOID)
 {
     ULONG Cr0;
@@ -568,6 +833,7 @@ KiSetCR0Bits(VOID)
 
 VOID
 NTAPI
+INIT_FUNCTION
 KiInitializeTSS2(IN PKTSS Tss,
                  IN PKGDTENTRY TssEntry OPTIONAL)
 {
@@ -582,11 +848,12 @@ KiInitializeTSS2(IN PKTSS Tss,
     }
 
     /* Now clear the I/O Map */
-    RtlFillMemory(Tss->IoMaps[0].IoMap, 8096, -1);
+    ASSERT(IOPM_COUNT == 1);
+    RtlFillMemory(Tss->IoMaps[0].IoMap, IOPM_FULL_SIZE, 0xFF);
 
     /* Initialize Interrupt Direction Maps */
     p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);
-    RtlZeroMemory(p, 32);
+    RtlZeroMemory(p, IOPM_DIRECTION_MAP_SIZE);
 
     /* Add DPMI support for interrupts */
     p[0] = 4;
@@ -595,7 +862,7 @@ KiInitializeTSS2(IN PKTSS Tss,
 
     /* Initialize the default Interrupt Direction Map */
     p = Tss->IntDirectionMap;
-    RtlZeroMemory(Tss->IntDirectionMap, 32);
+    RtlZeroMemory(Tss->IntDirectionMap, IOPM_DIRECTION_MAP_SIZE);
 
     /* Add DPMI support */
     p[0] = 4;
@@ -620,6 +887,7 @@ KiInitializeTSS(IN PKTSS Tss)
 
 VOID
 FASTCALL
+INIT_FUNCTION
 Ki386InitializeTss(IN PKTSS Tss,
                    IN PKIDTENTRY Idt,
                    IN PKGDTENTRY Gdt)
@@ -648,9 +916,9 @@ Ki386InitializeTss(IN PKTSS Tss,
     Tss = (PKTSS)KiDoubleFaultTSS;
     KiInitializeTSS(Tss);
     Tss->CR3 = __readcr3();
-    Tss->Esp0 = PtrToUlong(KiDoubleFaultStack);
-    Tss->Esp = PtrToUlong(KiDoubleFaultStack);
-    Tss->Eip = PtrToUlong(KiTrap8);
+    Tss->Esp0 = KiDoubleFaultStack;
+    Tss->Esp = KiDoubleFaultStack;
+    Tss->Eip = PtrToUlong(KiTrap08);
     Tss->Cs = KGDT_R0_CODE;
     Tss->Fs = KGDT_R0_PCR;
     Tss->Ss = Ke386GetSs();
@@ -678,9 +946,9 @@ Ki386InitializeTss(IN PKTSS Tss,
     Tss = (PKTSS)KiNMITSS;
     KiInitializeTSS(Tss);
     Tss->CR3 = __readcr3();
-    Tss->Esp0 = PtrToUlong(KiDoubleFaultStack);
-    Tss->Esp = PtrToUlong(KiDoubleFaultStack);
-    Tss->Eip = PtrToUlong(KiTrap2);
+    Tss->Esp0 = KiDoubleFaultStack;
+    Tss->Esp = KiDoubleFaultStack;
+    Tss->Eip = PtrToUlong(KiTrap02);
     Tss->Cs = KGDT_R0_CODE;
     Tss->Fs = KGDT_R0_PCR;
     Tss->Ss = Ke386GetSs();
@@ -779,6 +1047,7 @@ KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState)
 
 VOID
 NTAPI
+INIT_FUNCTION
 KiInitializeMachineType(VOID)
 {
     /* Set the Machine Type we got from NTLDR */
@@ -787,6 +1056,7 @@ KiInitializeMachineType(VOID)
 
 ULONG_PTR
 NTAPI
+INIT_FUNCTION
 KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
 {
     /* Set CS and ESP */
@@ -800,20 +1070,41 @@ KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
 
 VOID
 NTAPI
+INIT_FUNCTION
 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);
+        /* Check if it has been disabled */
+        if (!KiFastSystemCallDisable)
+        {
+            /* Do an IPI to enable it */
+            KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters, 0);
+
+            /* It's enabled, so use the proper exit stub */
+            KiFastCallExitHandler = KiSystemCallSysExitReturn;
+            DPRINT1("Support for SYSENTER detected.\n");
+        }
+        else
+        {
+            /* Disable fast system call */
+            KeFeatureBits &= ~KF_FAST_SYSCALL;
+            KiFastCallExitHandler = KiSystemCallTrapReturn;
+            DPRINT1("Support for SYSENTER disabled.\n");
+        }
+    }
+    else
+    {
+        /* Use the IRET handler */
+        KiFastCallExitHandler = KiSystemCallTrapReturn;
+        DPRINT1("No support for SYSENTER detected.\n");
     }
 }
 
 ULONG_PTR
 NTAPI
+INIT_FUNCTION
 Ki386EnableDE(IN ULONG_PTR Context)
 {
     /* Enable DE */
@@ -823,6 +1114,7 @@ Ki386EnableDE(IN ULONG_PTR Context)
 
 ULONG_PTR
 NTAPI
+INIT_FUNCTION
 Ki386EnableFxsr(IN ULONG_PTR Context)
 {
     /* Enable FXSR */
@@ -832,17 +1124,18 @@ Ki386EnableFxsr(IN ULONG_PTR Context)
 
 ULONG_PTR
 NTAPI
+INIT_FUNCTION
 Ki386EnableXMMIExceptions(IN ULONG_PTR Context)
 {
     PKIDTENTRY IdtEntry;
 
-    /* Get the IDT Entry for Interrupt 19 */
-    IdtEntry = &((PKIPCR)KeGetPcr())->IDT[19];
+    /* Get the IDT Entry for Interrupt 0x13 */
+    IdtEntry = &((PKIPCR)KeGetPcr())->IDT[0x13];
 
     /* Set it up */
     IdtEntry->Selector = KGDT_R0_CODE;
-    IdtEntry->Offset = ((ULONG_PTR)KiTrap19 & 0xFFFF);
-    IdtEntry->ExtendedOffset = ((ULONG_PTR)KiTrap19 >> 16) & 0xFFFF;
+    IdtEntry->Offset = ((ULONG_PTR)KiTrap13 & 0xFFFF);
+    IdtEntry->ExtendedOffset = ((ULONG_PTR)KiTrap13 >> 16) & 0xFFFF;
     ((PKIDT_ACCESS)&IdtEntry->Access)->Dpl = 0;
     ((PKIDT_ACCESS)&IdtEntry->Access)->Present = 1;
     ((PKIDT_ACCESS)&IdtEntry->Access)->SegmentType = I386_INTERRUPT_GATE;
@@ -854,6 +1147,7 @@ Ki386EnableXMMIExceptions(IN ULONG_PTR Context)
 
 VOID
 NTAPI
+INIT_FUNCTION
 KiI386PentiumLockErrataFixup(VOID)
 {
     KDESCRIPTOR IdtDescriptor;
@@ -923,8 +1217,201 @@ KeZeroPages(IN PVOID Address,
     RtlZeroMemory(Address, Size);
 }
 
+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);
+}
+
+BOOLEAN
+NTAPI
+INIT_FUNCTION
+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 */
+#ifdef _MSC_VER
+    __asm fninit;
+    __asm fnstsw Magic;
+#else
+    asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic));
+#endif
+    
+    /* 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
+INIT_FUNCTION
+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 */
+    Ke386FnInit();
+    
+    /* 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;
+}
+
+VOID
+NTAPI
+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);
+}
+
 /* PUBLIC FUNCTIONS **********************************************************/
 
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+KiCoprocessorError(VOID)
+{
+    PFX_SAVE_AREA NpxArea;
+    
+    /* Get the FPU area */
+    NpxArea = KiGetThreadNpxArea(KeGetCurrentThread());
+    
+    /* Set CR0_TS */
+    NpxArea->Cr0NpxState = CR0_TS;
+    __writecr0(__readcr0() | CR0_TS);
+}
+
 /*
  * @implemented
  */