xref: /reactos/ntoskrnl/ke/i386/cpu.c (revision 02e84521)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/i386/cpu.c
5  * PURPOSE:         Routines for CPU-level support
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS *******************************************************************/
16 
17 /* The TSS to use for Double Fault Traps (INT 0x9) */
18 UCHAR KiDoubleFaultTSS[KTSS_IO_MAPS];
19 
20 /* The TSS to use for NMI Fault Traps (INT 0x2) */
21 UCHAR KiNMITSS[KTSS_IO_MAPS];
22 
23 /* CPU Features and Flags */
24 ULONG KeI386CpuType;
25 ULONG KeI386CpuStep;
26 ULONG KiFastSystemCallDisable = 0;
27 ULONG KeI386NpxPresent = 0;
28 ULONG KiMXCsrMask = 0;
29 ULONG MxcsrFeatureMask = 0;
30 ULONG KeI386XMMIPresent = 0;
31 ULONG KeI386FxsrPresent = 0;
32 ULONG KeI386MachineType;
33 ULONG Ke386Pae = FALSE;
34 ULONG Ke386NoExecute = FALSE;
35 ULONG KeLargestCacheLine = 0x40;
36 ULONG KeDcacheFlushCount = 0;
37 ULONG KeIcacheFlushCount = 0;
38 ULONG KiDmaIoCoherency = 0;
39 ULONG KePrefetchNTAGranularity = 32;
40 BOOLEAN KiI386PentiumLockErrataPresent;
41 BOOLEAN KiSMTProcessorsPresent;
42 
43 /* The distance between SYSEXIT and IRETD return modes */
44 UCHAR KiSystemCallExitAdjust;
45 
46 /* The offset that was applied -- either 0 or the value above */
47 UCHAR KiSystemCallExitAdjusted;
48 
49 /* Whether the adjustment was already done once */
50 BOOLEAN KiFastCallCopyDoneOnce;
51 
52 /* Flush data */
53 volatile LONG KiTbFlushTimeStamp;
54 
55 /* CPU Signatures */
56 static const CHAR CmpIntelID[]       = "GenuineIntel";
57 static const CHAR CmpAmdID[]         = "AuthenticAMD";
58 static const CHAR CmpCyrixID[]       = "CyrixInstead";
59 static const CHAR CmpTransmetaID[]   = "GenuineTMx86";
60 static const CHAR CmpCentaurID[]     = "CentaurHauls";
61 static const CHAR CmpRiseID[]        = "RiseRiseRise";
62 
63 /* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/
64 
65 /* NSC/Cyrix CPU configuration register index */
66 #define CX86_CCR1 0xc1
67 
68 /* NSC/Cyrix CPU indexed register access macros */
69 static __inline
70 UCHAR
71 getCx86(UCHAR reg)
72 {
73     WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x22, reg);
74     return READ_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x23);
75 }
76 
77 static __inline
78 void
79 setCx86(UCHAR reg, UCHAR data)
80 {
81     WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x22, reg);
82     WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x23, data);
83 }
84 
85 
86 /* FUNCTIONS *****************************************************************/
87 
88 INIT_FUNCTION
89 VOID
90 NTAPI
91 KiSetProcessorType(VOID)
92 {
93     ULONG EFlags, NewEFlags;
94     CPU_INFO CpuInfo;
95     ULONG Stepping, Type;
96 
97     /* Start by assuming no CPUID data */
98     KeGetCurrentPrcb()->CpuID = 0;
99 
100     /* Save EFlags */
101     EFlags = __readeflags();
102 
103     /* XOR out the ID bit and update EFlags */
104     NewEFlags = EFlags ^ EFLAGS_ID;
105     __writeeflags(NewEFlags);
106 
107     /* Get them back and see if they were modified */
108     NewEFlags = __readeflags();
109     if (NewEFlags != EFlags)
110     {
111         /* The modification worked, so CPUID exists. Set the ID Bit again. */
112         EFlags |= EFLAGS_ID;
113         __writeeflags(EFlags);
114 
115         /* Peform CPUID 0 to see if CPUID 1 is supported */
116         KiCpuId(&CpuInfo, 0);
117         if (CpuInfo.Eax > 0)
118         {
119             /* Do CPUID 1 now */
120             KiCpuId(&CpuInfo, 1);
121 
122             /*
123              * Get the Stepping and Type. The stepping contains both the
124              * Model and the Step, while the Type contains the returned Type.
125              * We ignore the family.
126              *
127              * For the stepping, we convert this: zzzzzzxy into this: x0y
128              */
129             Stepping = CpuInfo.Eax & 0xF0;
130             Stepping <<= 4;
131             Stepping += (CpuInfo.Eax & 0xFF);
132             Stepping &= 0xF0F;
133             Type = CpuInfo.Eax & 0xF00;
134             Type >>= 8;
135 
136             /* Save them in the PRCB */
137             KeGetCurrentPrcb()->CpuID = TRUE;
138             KeGetCurrentPrcb()->CpuType = (UCHAR)Type;
139             KeGetCurrentPrcb()->CpuStep = (USHORT)Stepping;
140         }
141         else
142         {
143             DPRINT1("CPUID Support lacking\n");
144         }
145     }
146     else
147     {
148         DPRINT1("CPUID Support lacking\n");
149     }
150 
151     /* Restore EFLAGS */
152     __writeeflags(EFlags);
153 }
154 
155 INIT_FUNCTION
156 ULONG
157 NTAPI
158 KiGetCpuVendor(VOID)
159 {
160     PKPRCB Prcb = KeGetCurrentPrcb();
161     CPU_INFO CpuInfo;
162 
163     /* Assume no Vendor ID and fail if no CPUID Support. */
164     Prcb->VendorString[0] = 0;
165     if (!Prcb->CpuID) return 0;
166 
167     /* Get the Vendor ID */
168     KiCpuId(&CpuInfo, 0);
169 
170     /* Copy it to the PRCB and null-terminate it */
171     *(ULONG*)&Prcb->VendorString[0] = CpuInfo.Ebx;
172     *(ULONG*)&Prcb->VendorString[4] = CpuInfo.Edx;
173     *(ULONG*)&Prcb->VendorString[8] = CpuInfo.Ecx;
174     Prcb->VendorString[12] = 0;
175 
176     /* Now check the CPU Type */
177     if (!strcmp(Prcb->VendorString, CmpIntelID))
178     {
179         return CPU_INTEL;
180     }
181     else if (!strcmp(Prcb->VendorString, CmpAmdID))
182     {
183         return CPU_AMD;
184     }
185     else if (!strcmp(Prcb->VendorString, CmpCyrixID))
186     {
187         DPRINT1("Cyrix CPU support not fully tested!\n");
188         return CPU_CYRIX;
189     }
190     else if (!strcmp(Prcb->VendorString, CmpTransmetaID))
191     {
192         DPRINT1("Transmeta CPU support not fully tested!\n");
193         return CPU_TRANSMETA;
194     }
195     else if (!strcmp(Prcb->VendorString, CmpCentaurID))
196     {
197         DPRINT1("Centaur CPU support not fully tested!\n");
198         return CPU_CENTAUR;
199     }
200     else if (!strcmp(Prcb->VendorString, CmpRiseID))
201     {
202         DPRINT1("Rise CPU support not fully tested!\n");
203         return CPU_RISE;
204     }
205 
206     /* Unknown CPU */
207     DPRINT1("%s CPU support not fully tested!\n", Prcb->VendorString);
208     return CPU_UNKNOWN;
209 }
210 
211 INIT_FUNCTION
212 ULONG
213 NTAPI
214 KiGetFeatureBits(VOID)
215 {
216     PKPRCB Prcb = KeGetCurrentPrcb();
217     ULONG Vendor;
218     ULONG FeatureBits = KF_WORKING_PTE;
219     CPU_INFO CpuInfo, DummyCpuInfo;
220     UCHAR Ccr1;
221     BOOLEAN ExtendedCPUID = TRUE;
222     ULONG CpuFeatures = 0;
223 
224     /* Get the Vendor ID */
225     Vendor = KiGetCpuVendor();
226 
227     /* Make sure we got a valid vendor ID at least. */
228     if (!Vendor) return FeatureBits;
229 
230     /* Get the CPUID Info. Features are in Reg[3]. */
231     KiCpuId(&CpuInfo, 1);
232 
233     /* Set the initial APIC ID */
234     Prcb->InitialApicId = (UCHAR)(CpuInfo.Ebx >> 24);
235 
236     switch (Vendor)
237     {
238         /* Intel CPUs */
239         case CPU_INTEL:
240 
241             /* Check if it's a P6 */
242             if (Prcb->CpuType == 6)
243             {
244                 /* Perform the special sequence to get the MicroCode Signature */
245                 __writemsr(0x8B, 0);
246                 KiCpuId(&DummyCpuInfo, 1);
247                 Prcb->UpdateSignature.QuadPart = __readmsr(0x8B);
248             }
249             else if (Prcb->CpuType == 5)
250             {
251                 /* On P5, enable workaround for the LOCK errata. */
252                 KiI386PentiumLockErrataPresent = TRUE;
253             }
254 
255             /* Check for broken P6 with bad SMP PTE implementation */
256             if (((CpuInfo.Eax & 0x0FF0) == 0x0610 && (CpuInfo.Eax & 0x000F) <= 0x9) ||
257                 ((CpuInfo.Eax & 0x0FF0) == 0x0630 && (CpuInfo.Eax & 0x000F) <= 0x4))
258             {
259                 /* Remove support for correct PTE support. */
260                 FeatureBits &= ~KF_WORKING_PTE;
261             }
262 
263             /* Check if the CPU is too old to support SYSENTER */
264             if ((Prcb->CpuType < 6) ||
265                 ((Prcb->CpuType == 6) && (Prcb->CpuStep < 0x0303)))
266             {
267                 /* Disable it */
268                 CpuInfo.Edx &= ~0x800;
269             }
270 
271             break;
272 
273         /* AMD CPUs */
274         case CPU_AMD:
275 
276             /* Check if this is a K5 or K6. (family 5) */
277             if ((CpuInfo.Eax & 0x0F00) == 0x0500)
278             {
279                 /* Get the Model Number */
280                 switch (CpuInfo.Eax & 0x00F0)
281                 {
282                     /* Model 1: K5 - 5k86 (initial models) */
283                     case 0x0010:
284 
285                         /* Check if this is Step 0 or 1. They don't support PGE */
286                         if ((CpuInfo.Eax & 0x000F) > 0x03) break;
287 
288                     /* Model 0: K5 - SSA5 */
289                     case 0x0000:
290 
291                         /* Model 0 doesn't support PGE at all. */
292                         CpuInfo.Edx &= ~0x2000;
293                         break;
294 
295                     /* Model 8: K6-2 */
296                     case 0x0080:
297 
298                         /* K6-2, Step 8 and over have support for MTRR. */
299                         if ((CpuInfo.Eax & 0x000F) >= 0x8) FeatureBits |= KF_AMDK6MTRR;
300                         break;
301 
302                     /* Model 9: K6-III
303                        Model D: K6-2+, K6-III+ */
304                     case 0x0090:
305                     case 0x00D0:
306 
307                         FeatureBits |= KF_AMDK6MTRR;
308                         break;
309                 }
310             }
311             else if((CpuInfo.Eax & 0x0F00) < 0x0500)
312             {
313                 /* Families below 5 don't support PGE, PSE or CMOV at all */
314                 CpuInfo.Edx &= ~(0x08 | 0x2000 | 0x8000);
315 
316                 /* They also don't support advanced CPUID functions. */
317                 ExtendedCPUID = FALSE;
318             }
319 
320             break;
321 
322         /* Cyrix CPUs */
323         case CPU_CYRIX:
324 
325             /* Workaround the "COMA" bug on 6x family of Cyrix CPUs */
326             if (Prcb->CpuType == 6 &&
327                 Prcb->CpuStep <= 1)
328             {
329                 /* Get CCR1 value */
330                 Ccr1 = getCx86(CX86_CCR1);
331 
332                 /* Enable the NO_LOCK bit */
333                 Ccr1 |= 0x10;
334 
335                 /* Set the new CCR1 value */
336                 setCx86(CX86_CCR1, Ccr1);
337             }
338 
339             break;
340 
341         /* Transmeta CPUs */
342         case CPU_TRANSMETA:
343 
344             /* Enable CMPXCHG8B if the family (>= 5), model and stepping (>= 4.2) support it */
345             if ((CpuInfo.Eax & 0x0FFF) >= 0x0542)
346             {
347                 __writemsr(0x80860004, __readmsr(0x80860004) | 0x0100);
348                 FeatureBits |= KF_CMPXCHG8B;
349             }
350 
351             break;
352 
353         /* Centaur, IDT, Rise and VIA CPUs */
354         case CPU_CENTAUR:
355         case CPU_RISE:
356 
357             /* These CPUs don't report the presence of CMPXCHG8B through CPUID.
358                However, this feature exists and operates properly without any additional steps. */
359             FeatureBits |= KF_CMPXCHG8B;
360 
361             break;
362     }
363 
364     /* Set the current features */
365     CpuFeatures = CpuInfo.Edx;
366 
367     /* Convert all CPUID Feature bits into our format */
368     if (CpuFeatures & 0x00000002) FeatureBits |= KF_V86_VIS | KF_CR4;
369     if (CpuFeatures & 0x00000008) FeatureBits |= KF_LARGE_PAGE | KF_CR4;
370     if (CpuFeatures & 0x00000010) FeatureBits |= KF_RDTSC;
371     if (CpuFeatures & 0x00000100) FeatureBits |= KF_CMPXCHG8B;
372     if (CpuFeatures & 0x00000800) FeatureBits |= KF_FAST_SYSCALL;
373     if (CpuFeatures & 0x00001000) FeatureBits |= KF_MTRR;
374     if (CpuFeatures & 0x00002000) FeatureBits |= KF_GLOBAL_PAGE | KF_CR4;
375     if (CpuFeatures & 0x00008000) FeatureBits |= KF_CMOV;
376     if (CpuFeatures & 0x00010000) FeatureBits |= KF_PAT;
377     if (CpuFeatures & 0x00200000) FeatureBits |= KF_DTS;
378     if (CpuFeatures & 0x00800000) FeatureBits |= KF_MMX;
379     if (CpuFeatures & 0x01000000) FeatureBits |= KF_FXSR;
380     if (CpuFeatures & 0x02000000) FeatureBits |= KF_XMMI;
381     if (CpuFeatures & 0x04000000) FeatureBits |= KF_XMMI64;
382 
383     /* Check if the CPU has hyper-threading */
384     if (CpuFeatures & 0x10000000)
385     {
386         /* Set the number of logical CPUs */
387         Prcb->LogicalProcessorsPerPhysicalProcessor = (UCHAR)(CpuInfo.Ebx >> 16);
388         if (Prcb->LogicalProcessorsPerPhysicalProcessor > 1)
389         {
390             /* We're on dual-core */
391             KiSMTProcessorsPresent = TRUE;
392         }
393     }
394     else
395     {
396         /* We only have a single CPU */
397         Prcb->LogicalProcessorsPerPhysicalProcessor = 1;
398     }
399 
400     /* Check if CPUID 0x80000000 is supported */
401     if (ExtendedCPUID)
402     {
403         /* Do the call */
404         KiCpuId(&CpuInfo, 0x80000000);
405         if ((CpuInfo.Eax & 0xffffff00) == 0x80000000)
406         {
407             /* Check if CPUID 0x80000001 is supported */
408             if (CpuInfo.Eax >= 0x80000001)
409             {
410                 /* Check which extended features are available. */
411                 KiCpuId(&CpuInfo, 0x80000001);
412 
413                 /* Check if NX-bit is supported */
414                 if (CpuInfo.Edx & 0x00100000) FeatureBits |= KF_NX_BIT;
415 
416                 /* Now handle each features for each CPU Vendor */
417                 switch (Vendor)
418                 {
419                     case CPU_AMD:
420                     case CPU_CENTAUR:
421                         if (CpuInfo.Edx & 0x80000000) FeatureBits |= KF_3DNOW;
422                         break;
423                 }
424             }
425         }
426     }
427 
428 #define print_supported(kf_value) ((FeatureBits & kf_value) ? #kf_value : "")
429     DPRINT1("Supported CPU features : %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
430     print_supported(KF_V86_VIS),
431     print_supported(KF_RDTSC),
432     print_supported(KF_CR4),
433     print_supported(KF_CMOV),
434     print_supported(KF_GLOBAL_PAGE),
435     print_supported(KF_LARGE_PAGE),
436     print_supported(KF_MTRR),
437     print_supported(KF_CMPXCHG8B),
438     print_supported(KF_MMX),
439     print_supported(KF_WORKING_PTE),
440     print_supported(KF_PAT),
441     print_supported(KF_FXSR),
442     print_supported(KF_FAST_SYSCALL),
443     print_supported(KF_XMMI),
444     print_supported(KF_3DNOW),
445     print_supported(KF_AMDK6MTRR),
446     print_supported(KF_XMMI64),
447     print_supported(KF_DTS),
448     print_supported(KF_NX_BIT),
449     print_supported(KF_NX_DISABLED),
450     print_supported(KF_NX_ENABLED));
451 #undef print_supported
452 
453     /* Return the Feature Bits */
454     return FeatureBits;
455 }
456 
457 INIT_FUNCTION
458 VOID
459 NTAPI
460 KiGetCacheInformation(VOID)
461 {
462     PKIPCR Pcr = (PKIPCR)KeGetPcr();
463     ULONG Vendor;
464     CPU_INFO CpuInfo;
465     ULONG CacheRequests = 0, i;
466     ULONG CurrentRegister;
467     UCHAR RegisterByte, Associativity = 0;
468     ULONG Size, CacheLine = 64, CurrentSize = 0;
469     BOOLEAN FirstPass = TRUE;
470 
471     /* Set default L2 size */
472     Pcr->SecondLevelCacheSize = 0;
473 
474     /* Get the Vendor ID and make sure we support CPUID */
475     Vendor = KiGetCpuVendor();
476     if (!Vendor) return;
477 
478     /* Check the Vendor ID */
479     switch (Vendor)
480     {
481         /* Handle Intel case */
482         case CPU_INTEL:
483 
484             /*Check if we support CPUID 2 */
485             KiCpuId(&CpuInfo, 0);
486             if (CpuInfo.Eax >= 2)
487             {
488                 /* We need to loop for the number of times CPUID will tell us to */
489                 do
490                 {
491                     /* Do the CPUID call */
492                     KiCpuId(&CpuInfo, 2);
493 
494                     /* Check if it was the first call */
495                     if (FirstPass)
496                     {
497                         /*
498                          * The number of times to loop is the first byte. Read
499                          * it and then destroy it so we don't get confused.
500                          */
501                         CacheRequests = CpuInfo.Eax & 0xFF;
502                         CpuInfo.Eax &= 0xFFFFFF00;
503 
504                         /* Don't go over this again */
505                         FirstPass = FALSE;
506                     }
507 
508                     /* Loop all 4 registers */
509                     for (i = 0; i < 4; i++)
510                     {
511                         /* Get the current register */
512                         CurrentRegister = CpuInfo.AsUINT32[i];
513 
514                         /*
515                          * If the upper bit is set, then this register should
516                          * be skipped.
517                          */
518                         if (CurrentRegister & 0x80000000) continue;
519 
520                         /* Keep looping for every byte inside this register */
521                         while (CurrentRegister)
522                         {
523                             /* Read a byte, skip a byte. */
524                             RegisterByte = (UCHAR)(CurrentRegister & 0xFF);
525                             CurrentRegister >>= 8;
526                             if (!RegisterByte) continue;
527 
528                             Size = 0;
529                             switch (RegisterByte)
530                             {
531                                 case 0x06:
532                                 case 0x08:
533                                     KePrefetchNTAGranularity = 32;
534                                     break;
535                                 case 0x09:
536                                     KePrefetchNTAGranularity = 64;
537                                     break;
538                                 case 0x0a:
539                                 case 0x0c:
540                                     KePrefetchNTAGranularity = 32;
541                                     break;
542                                 case 0x0d:
543                                 case 0x0e:
544                                     KePrefetchNTAGranularity = 64;
545                                     break;
546                                 case 0x1d:
547                                     Size = 128 * 1024;
548                                     Associativity = 2;
549                                     break;
550                                 case 0x21:
551                                     Size = 256 * 1024;
552                                     Associativity = 8;
553                                     break;
554                                 case 0x24:
555                                     Size = 1024 * 1024;
556                                     Associativity = 16;
557                                     break;
558                                 case 0x2c:
559                                 case 0x30:
560                                     KePrefetchNTAGranularity = 64;
561                                     break;
562                                 case 0x41:
563                                 case 0x42:
564                                 case 0x43:
565                                 case 0x44:
566                                 case 0x45:
567                                     Size = (1 << (RegisterByte - 0x41)) * 128 * 1024;
568                                     Associativity = 4;
569                                     break;
570                                 case 0x48:
571                                     Size = 3 * 1024 * 1024;
572                                     Associativity = 12;
573                                     break;
574                                 case 0x49:
575                                     Size = 4 * 1024 * 1024;
576                                     Associativity = 16;
577                                     break;
578                                 case 0x4e:
579                                     Size = 6 * 1024 * 1024;
580                                     Associativity = 24;
581                                     break;
582                                 case 0x60:
583                                 case 0x66:
584                                 case 0x67:
585                                 case 0x68:
586                                     KePrefetchNTAGranularity = 64;
587                                     break;
588                                 case 0x78:
589                                     Size = 1024 * 1024;
590                                     Associativity = 4;
591                                     break;
592                                 case 0x79:
593                                 case 0x7a:
594                                 case 0x7b:
595                                 case 0x7c:
596                                 case 0x7d:
597                                     Size = (1 << (RegisterByte - 0x79)) * 128 * 1024;
598                                     Associativity = 8;
599                                     break;
600                                 case 0x7f:
601                                     Size = 512 * 1024;
602                                     Associativity = 2;
603                                     break;
604                                 case 0x80:
605                                     Size = 512 * 1024;
606                                     Associativity = 8;
607                                     break;
608                                 case 0x82:
609                                 case 0x83:
610                                 case 0x84:
611                                 case 0x85:
612                                     Size = (1 << (RegisterByte - 0x82)) * 256 * 1024;
613                                     Associativity = 8;
614                                     break;
615                                 case 0x86:
616                                     Size = 512 * 1024;
617                                     Associativity = 4;
618                                     break;
619                                 case 0x87:
620                                     Size = 1024 * 1024;
621                                     Associativity = 8;
622                                     break;
623                                 case 0xf0:
624                                     KePrefetchNTAGranularity = 64;
625                                     break;
626                                 case 0xf1:
627                                     KePrefetchNTAGranularity = 128;
628                                     break;
629                             }
630                             if (Size && (Size / Associativity) > CurrentSize)
631                             {
632                                 /* Set the L2 Cache Size and Associativity */
633                                 CurrentSize = Size / Associativity;
634                                 Pcr->SecondLevelCacheSize = Size;
635                                 Pcr->SecondLevelCacheAssociativity = Associativity;
636                             }
637                         }
638                     }
639                 } while (--CacheRequests);
640             }
641             break;
642 
643         case CPU_AMD:
644 
645             /* Check if we support CPUID 0x80000005 */
646             KiCpuId(&CpuInfo, 0x80000000);
647             if (CpuInfo.Eax >= 0x80000005)
648             {
649                 /* Get L1 size first */
650                 KiCpuId(&CpuInfo, 0x80000005);
651                 KePrefetchNTAGranularity = CpuInfo.Ecx & 0xFF;
652 
653                 /* Check if we support CPUID 0x80000006 */
654                 KiCpuId(&CpuInfo, 0x80000000);
655                 if (CpuInfo.Eax >= 0x80000006)
656                 {
657                     /* Get 2nd level cache and tlb size */
658                     KiCpuId(&CpuInfo, 0x80000006);
659 
660                     /* Cache line size */
661                     CacheLine = CpuInfo.Ecx & 0xFF;
662 
663                     /* Hardcode associativity */
664                     RegisterByte = (CpuInfo.Ecx >> 12) & 0xFF;
665                     switch (RegisterByte)
666                     {
667                         case 2:
668                             Associativity = 2;
669                             break;
670 
671                         case 4:
672                             Associativity = 4;
673                             break;
674 
675                         case 6:
676                             Associativity = 8;
677                             break;
678 
679                         case 8:
680                         case 15:
681                             Associativity = 16;
682                             break;
683 
684                         default:
685                             Associativity = 1;
686                             break;
687                     }
688 
689                     /* Compute size */
690                     Size = (CpuInfo.Ecx >> 16) << 10;
691 
692                     /* Hack for Model 6, Steping 300 */
693                     if ((KeGetCurrentPrcb()->CpuType == 6) &&
694                         (KeGetCurrentPrcb()->CpuStep == 0x300))
695                     {
696                         /* Stick 64K in there */
697                         Size = 64 * 1024;
698                     }
699 
700                     /* Set the L2 Cache Size and associativity */
701                     Pcr->SecondLevelCacheSize = Size;
702                     Pcr->SecondLevelCacheAssociativity = Associativity;
703                 }
704             }
705             break;
706 
707         case CPU_CYRIX:
708         case CPU_TRANSMETA:
709         case CPU_CENTAUR:
710         case CPU_RISE:
711 
712             /* FIXME */
713             break;
714     }
715 
716     /* Set the cache line */
717     if (CacheLine > KeLargestCacheLine) KeLargestCacheLine = CacheLine;
718     DPRINT1("Prefetch Cache: %lu bytes\tL2 Cache: %lu bytes\tL2 Cache Line: %lu bytes\tL2 Cache Associativity: %lu\n",
719             KePrefetchNTAGranularity,
720             Pcr->SecondLevelCacheSize,
721             KeLargestCacheLine,
722             Pcr->SecondLevelCacheAssociativity);
723 }
724 
725 INIT_FUNCTION
726 VOID
727 NTAPI
728 KiSetCR0Bits(VOID)
729 {
730     ULONG Cr0;
731 
732     /* Save current CR0 */
733     Cr0 = __readcr0();
734 
735     /* If this is a 486, enable Write-Protection */
736     if (KeGetCurrentPrcb()->CpuType > 3) Cr0 |= CR0_WP;
737 
738     /* Set new Cr0 */
739     __writecr0(Cr0);
740 }
741 
742 INIT_FUNCTION
743 VOID
744 NTAPI
745 KiInitializeTSS2(IN PKTSS Tss,
746                  IN PKGDTENTRY TssEntry OPTIONAL)
747 {
748     PUCHAR p;
749 
750     /* Make sure the GDT Entry is valid */
751     if (TssEntry)
752     {
753         /* Set the Limit */
754         TssEntry->LimitLow = sizeof(KTSS) - 1;
755         TssEntry->HighWord.Bits.LimitHi = 0;
756     }
757 
758     /* Now clear the I/O Map */
759     ASSERT(IOPM_COUNT == 1);
760     RtlFillMemory(Tss->IoMaps[0].IoMap, IOPM_FULL_SIZE, 0xFF);
761 
762     /* Initialize Interrupt Direction Maps */
763     p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);
764     RtlZeroMemory(p, IOPM_DIRECTION_MAP_SIZE);
765 
766     /* Add DPMI support for interrupts */
767     p[0] = 4;
768     p[3] = 0x18;
769     p[4] = 0x18;
770 
771     /* Initialize the default Interrupt Direction Map */
772     p = Tss->IntDirectionMap;
773     RtlZeroMemory(Tss->IntDirectionMap, IOPM_DIRECTION_MAP_SIZE);
774 
775     /* Add DPMI support */
776     p[0] = 4;
777     p[3] = 0x18;
778     p[4] = 0x18;
779 }
780 
781 VOID
782 NTAPI
783 KiInitializeTSS(IN PKTSS Tss)
784 {
785     /* Set an invalid map base */
786     Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
787 
788     /* Disable traps during Task Switches */
789     Tss->Flags = 0;
790 
791     /* Set LDT and Ring 0 SS */
792     Tss->LDT = 0;
793     Tss->Ss0 = KGDT_R0_DATA;
794 }
795 
796 INIT_FUNCTION
797 VOID
798 FASTCALL
799 Ki386InitializeTss(IN PKTSS Tss,
800                    IN PKIDTENTRY Idt,
801                    IN PKGDTENTRY Gdt)
802 {
803     PKGDTENTRY TssEntry, TaskGateEntry;
804 
805     /* Initialize the boot TSS. */
806     TssEntry = &Gdt[KGDT_TSS / sizeof(KGDTENTRY)];
807     TssEntry->HighWord.Bits.Type = I386_TSS;
808     TssEntry->HighWord.Bits.Pres = 1;
809     TssEntry->HighWord.Bits.Dpl = 0;
810     KiInitializeTSS2(Tss, TssEntry);
811     KiInitializeTSS(Tss);
812 
813     /* Load the task register */
814     Ke386SetTr(KGDT_TSS);
815 
816     /* Setup the Task Gate for Double Fault Traps */
817     TaskGateEntry = (PKGDTENTRY)&Idt[8];
818     TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
819     TaskGateEntry->HighWord.Bits.Pres = 1;
820     TaskGateEntry->HighWord.Bits.Dpl = 0;
821     ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_DF_TSS;
822 
823     /* Initialize the TSS used for handling double faults. */
824     Tss = (PKTSS)KiDoubleFaultTSS;
825     KiInitializeTSS(Tss);
826     Tss->CR3 = __readcr3();
827     Tss->Esp0 = KiDoubleFaultStack;
828     Tss->Esp = KiDoubleFaultStack;
829     Tss->Eip = PtrToUlong(KiTrap08);
830     Tss->Cs = KGDT_R0_CODE;
831     Tss->Fs = KGDT_R0_PCR;
832     Tss->Ss = Ke386GetSs();
833     Tss->Es = KGDT_R3_DATA | RPL_MASK;
834     Tss->Ds = KGDT_R3_DATA | RPL_MASK;
835 
836     /* Setup the Double Trap TSS entry in the GDT */
837     TssEntry = &Gdt[KGDT_DF_TSS / sizeof(KGDTENTRY)];
838     TssEntry->HighWord.Bits.Type = I386_TSS;
839     TssEntry->HighWord.Bits.Pres = 1;
840     TssEntry->HighWord.Bits.Dpl = 0;
841     TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
842     TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
843     TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
844     TssEntry->LimitLow = KTSS_IO_MAPS;
845 
846     /* Now setup the NMI Task Gate */
847     TaskGateEntry = (PKGDTENTRY)&Idt[2];
848     TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
849     TaskGateEntry->HighWord.Bits.Pres = 1;
850     TaskGateEntry->HighWord.Bits.Dpl = 0;
851     ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_NMI_TSS;
852 
853     /* Initialize the actual TSS */
854     Tss = (PKTSS)KiNMITSS;
855     KiInitializeTSS(Tss);
856     Tss->CR3 = __readcr3();
857     Tss->Esp0 = KiDoubleFaultStack;
858     Tss->Esp = KiDoubleFaultStack;
859     Tss->Eip = PtrToUlong(KiTrap02);
860     Tss->Cs = KGDT_R0_CODE;
861     Tss->Fs = KGDT_R0_PCR;
862     Tss->Ss = Ke386GetSs();
863     Tss->Es = KGDT_R3_DATA | RPL_MASK;
864     Tss->Ds = KGDT_R3_DATA | RPL_MASK;
865 
866     /* And its associated TSS Entry */
867     TssEntry = &Gdt[KGDT_NMI_TSS / sizeof(KGDTENTRY)];
868     TssEntry->HighWord.Bits.Type = I386_TSS;
869     TssEntry->HighWord.Bits.Pres = 1;
870     TssEntry->HighWord.Bits.Dpl = 0;
871     TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
872     TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
873     TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
874     TssEntry->LimitLow = KTSS_IO_MAPS;
875 }
876 
877 VOID
878 NTAPI
879 KeFlushCurrentTb(VOID)
880 {
881 
882 #if !defined(_GLOBAL_PAGES_ARE_AWESOME_)
883 
884     /* Flush the TLB by resetting CR3 */
885     __writecr3(__readcr3());
886 
887 #else
888 
889     /* Check if global pages are enabled */
890     if (KeFeatureBits & KF_GLOBAL_PAGE)
891     {
892         ULONG Cr4;
893 
894         /* Disable PGE (Note: may not have been enabled yet) */
895         Cr4 = __readcr4();
896         __writecr4(Cr4 & ~CR4_PGE);
897 
898         /* Flush everything */
899         __writecr3(__readcr3());
900 
901         /* Re-enable PGE */
902         __writecr4(Cr4);
903     }
904     else
905     {
906         /* No global pages, resetting CR3 is enough */
907         __writecr3(__readcr3());
908     }
909 
910 #endif
911 
912 }
913 
914 VOID
915 NTAPI
916 KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState)
917 {
918     PKGDTENTRY TssEntry;
919 
920     //
921     // Restore the CR registers
922     //
923     __writecr0(ProcessorState->SpecialRegisters.Cr0);
924     Ke386SetCr2(ProcessorState->SpecialRegisters.Cr2);
925     __writecr3(ProcessorState->SpecialRegisters.Cr3);
926     if (KeFeatureBits & KF_CR4) __writecr4(ProcessorState->SpecialRegisters.Cr4);
927 
928     //
929     // Restore the DR registers
930     //
931     __writedr(0, ProcessorState->SpecialRegisters.KernelDr0);
932     __writedr(1, ProcessorState->SpecialRegisters.KernelDr1);
933     __writedr(2, ProcessorState->SpecialRegisters.KernelDr2);
934     __writedr(3, ProcessorState->SpecialRegisters.KernelDr3);
935     __writedr(6, ProcessorState->SpecialRegisters.KernelDr6);
936     __writedr(7, ProcessorState->SpecialRegisters.KernelDr7);
937 
938     //
939     // Restore GDT and IDT
940     //
941     Ke386SetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
942     __lidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
943 
944     //
945     // Clear the busy flag so we don't crash if we reload the same selector
946     //
947     TssEntry = (PKGDTENTRY)(ProcessorState->SpecialRegisters.Gdtr.Base +
948                             ProcessorState->SpecialRegisters.Tr);
949     TssEntry->HighWord.Bytes.Flags1 &= ~0x2;
950 
951     //
952     // Restore TSS and LDT
953     //
954     Ke386SetTr(ProcessorState->SpecialRegisters.Tr);
955     Ke386SetLocalDescriptorTable(ProcessorState->SpecialRegisters.Ldtr);
956 }
957 
958 VOID
959 NTAPI
960 KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState)
961 {
962     /* Save the CR registers */
963     ProcessorState->SpecialRegisters.Cr0 = __readcr0();
964     ProcessorState->SpecialRegisters.Cr2 = __readcr2();
965     ProcessorState->SpecialRegisters.Cr3 = __readcr3();
966     ProcessorState->SpecialRegisters.Cr4 = (KeFeatureBits & KF_CR4) ?
967                                            __readcr4() : 0;
968 
969     /* Save the DR registers */
970     ProcessorState->SpecialRegisters.KernelDr0 = __readdr(0);
971     ProcessorState->SpecialRegisters.KernelDr1 = __readdr(1);
972     ProcessorState->SpecialRegisters.KernelDr2 = __readdr(2);
973     ProcessorState->SpecialRegisters.KernelDr3 = __readdr(3);
974     ProcessorState->SpecialRegisters.KernelDr6 = __readdr(6);
975     ProcessorState->SpecialRegisters.KernelDr7 = __readdr(7);
976     __writedr(7, 0);
977 
978     /* Save GDT, IDT, LDT and TSS */
979     Ke386GetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
980     __sidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
981     ProcessorState->SpecialRegisters.Tr = Ke386GetTr();
982     ProcessorState->SpecialRegisters.Ldtr = Ke386GetLocalDescriptorTable();
983 }
984 
985 INIT_FUNCTION
986 VOID
987 NTAPI
988 KiInitializeMachineType(VOID)
989 {
990     /* Set the Machine Type we got from NTLDR */
991     KeI386MachineType = KeLoaderBlock->u.I386.MachineType & 0x000FF;
992 }
993 
994 INIT_FUNCTION
995 ULONG_PTR
996 NTAPI
997 KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
998 {
999     /* Set CS and ESP */
1000     __writemsr(0x174, KGDT_R0_CODE);
1001     __writemsr(0x175, (ULONG_PTR)KeGetCurrentPrcb()->DpcStack);
1002 
1003     /* Set LSTAR */
1004     __writemsr(0x176, (ULONG_PTR)KiFastCallEntry);
1005     return 0;
1006 }
1007 
1008 INIT_FUNCTION
1009 VOID
1010 NTAPI
1011 KiRestoreFastSyscallReturnState(VOID)
1012 {
1013     /* Check if the CPU Supports fast system call */
1014     if (KeFeatureBits & KF_FAST_SYSCALL)
1015     {
1016         /* Check if it has been disabled */
1017         if (KiFastSystemCallDisable)
1018         {
1019             /* Disable fast system call */
1020             KeFeatureBits &= ~KF_FAST_SYSCALL;
1021             KiFastCallExitHandler = KiSystemCallTrapReturn;
1022             DPRINT1("Support for SYSENTER disabled.\n");
1023         }
1024         else
1025         {
1026             /* Do an IPI to enable it */
1027             KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters, 0);
1028 
1029             /* It's enabled, so use the proper exit stub */
1030             KiFastCallExitHandler = KiSystemCallSysExitReturn;
1031             DPRINT("Support for SYSENTER detected.\n");
1032         }
1033     }
1034     else
1035     {
1036         /* Use the IRET handler */
1037         KiFastCallExitHandler = KiSystemCallTrapReturn;
1038         DPRINT1("No support for SYSENTER detected.\n");
1039     }
1040 }
1041 
1042 INIT_FUNCTION
1043 ULONG_PTR
1044 NTAPI
1045 Ki386EnableDE(IN ULONG_PTR Context)
1046 {
1047     /* Enable DE */
1048     __writecr4(__readcr4() | CR4_DE);
1049     return 0;
1050 }
1051 
1052 INIT_FUNCTION
1053 ULONG_PTR
1054 NTAPI
1055 Ki386EnableFxsr(IN ULONG_PTR Context)
1056 {
1057     /* Enable FXSR */
1058     __writecr4(__readcr4() | CR4_FXSR);
1059     return 0;
1060 }
1061 
1062 INIT_FUNCTION
1063 ULONG_PTR
1064 NTAPI
1065 Ki386EnableXMMIExceptions(IN ULONG_PTR Context)
1066 {
1067     PKIDTENTRY IdtEntry;
1068 
1069     /* Get the IDT Entry for Interrupt 0x13 */
1070     IdtEntry = &((PKIPCR)KeGetPcr())->IDT[0x13];
1071 
1072     /* Set it up */
1073     IdtEntry->Selector = KGDT_R0_CODE;
1074     IdtEntry->Offset = ((ULONG_PTR)KiTrap13 & 0xFFFF);
1075     IdtEntry->ExtendedOffset = ((ULONG_PTR)KiTrap13 >> 16) & 0xFFFF;
1076     ((PKIDT_ACCESS)&IdtEntry->Access)->Dpl = 0;
1077     ((PKIDT_ACCESS)&IdtEntry->Access)->Present = 1;
1078     ((PKIDT_ACCESS)&IdtEntry->Access)->SegmentType = I386_INTERRUPT_GATE;
1079 
1080     /* Enable XMMI exceptions */
1081     __writecr4(__readcr4() | CR4_XMMEXCPT);
1082     return 0;
1083 }
1084 
1085 INIT_FUNCTION
1086 VOID
1087 NTAPI
1088 KiI386PentiumLockErrataFixup(VOID)
1089 {
1090     KDESCRIPTOR IdtDescriptor = {0, 0, 0};
1091     PKIDTENTRY NewIdt, NewIdt2;
1092 
1093     /* Allocate memory for a new IDT */
1094     NewIdt = ExAllocatePool(NonPagedPool, 2 * PAGE_SIZE);
1095 
1096     /* Put everything after the first 7 entries on a new page */
1097     NewIdt2 = (PVOID)((ULONG_PTR)NewIdt + PAGE_SIZE - (7 * sizeof(KIDTENTRY)));
1098 
1099     /* Disable interrupts */
1100     _disable();
1101 
1102     /* Get the current IDT and copy it */
1103     __sidt(&IdtDescriptor.Limit);
1104     RtlCopyMemory(NewIdt2,
1105                   (PVOID)IdtDescriptor.Base,
1106                   IdtDescriptor.Limit + 1);
1107     IdtDescriptor.Base = (ULONG)NewIdt2;
1108 
1109     /* Set the new IDT */
1110     __lidt(&IdtDescriptor.Limit);
1111     ((PKIPCR)KeGetPcr())->IDT = NewIdt2;
1112 
1113     /* Restore interrupts */
1114     _enable();
1115 
1116     /* Set the first 7 entries as read-only to produce a fault */
1117     MmSetPageProtect(NULL, NewIdt, PAGE_READONLY);
1118 }
1119 
1120 BOOLEAN
1121 NTAPI
1122 KeInvalidateAllCaches(VOID)
1123 {
1124     /* Only supported on Pentium Pro and higher */
1125     if (KeI386CpuType < 6) return FALSE;
1126 
1127     /* Invalidate all caches */
1128     __wbinvd();
1129     return TRUE;
1130 }
1131 
1132 VOID
1133 FASTCALL
1134 KeZeroPages(IN PVOID Address,
1135             IN ULONG Size)
1136 {
1137     /* Not using XMMI in this routine */
1138     RtlZeroMemory(Address, Size);
1139 }
1140 
1141 VOID
1142 NTAPI
1143 KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame,
1144                      IN PKEXCEPTION_FRAME ExceptionFrame)
1145 {
1146     PKPRCB Prcb = KeGetCurrentPrcb();
1147 
1148     //
1149     // Save full context
1150     //
1151     Prcb->ProcessorState.ContextFrame.ContextFlags = CONTEXT_FULL |
1152                                                      CONTEXT_DEBUG_REGISTERS;
1153     KeTrapFrameToContext(TrapFrame, NULL, &Prcb->ProcessorState.ContextFrame);
1154 
1155     //
1156     // Save control registers
1157     //
1158     KiSaveProcessorControlState(&Prcb->ProcessorState);
1159 }
1160 
1161 INIT_FUNCTION
1162 BOOLEAN
1163 NTAPI
1164 KiIsNpxPresent(VOID)
1165 {
1166     ULONG Cr0;
1167     USHORT Magic;
1168 
1169     /* Set magic */
1170     Magic = 0xFFFF;
1171 
1172     /* Read CR0 and mask out FPU flags */
1173     Cr0 = __readcr0() & ~(CR0_MP | CR0_TS | CR0_EM | CR0_ET);
1174 
1175     /* Store on FPU stack */
1176 #ifdef _MSC_VER
1177     __asm fninit;
1178     __asm fnstsw Magic;
1179 #else
1180     asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic));
1181 #endif
1182 
1183     /* Magic should now be cleared */
1184     if (Magic & 0xFF)
1185     {
1186         /* You don't have an FPU -- enable emulation for now */
1187         __writecr0(Cr0 | CR0_EM | CR0_TS);
1188         return FALSE;
1189     }
1190 
1191     /* You have an FPU, enable it */
1192     Cr0 |= CR0_ET;
1193 
1194     /* Enable INT 16 on 486 and higher */
1195     if (KeGetCurrentPrcb()->CpuType >= 3) Cr0 |= CR0_NE;
1196 
1197     /* Set FPU state */
1198     __writecr0(Cr0 | CR0_EM | CR0_TS);
1199     return TRUE;
1200 }
1201 
1202 INIT_FUNCTION
1203 BOOLEAN
1204 NTAPI
1205 KiIsNpxErrataPresent(VOID)
1206 {
1207     static double Value1 = 4195835.0, Value2 = 3145727.0;
1208     INT ErrataPresent;
1209     ULONG Cr0;
1210 
1211     /* Disable interrupts */
1212     _disable();
1213 
1214     /* Read CR0 and remove FPU flags */
1215     Cr0 = __readcr0();
1216     __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1217 
1218     /* Initialize FPU state */
1219     Ke386FnInit();
1220 
1221     /* Multiply the magic values and divide, we should get the result back */
1222 #ifdef __GNUC__
1223     __asm__ __volatile__
1224     (
1225         "fldl %1\n\t"
1226         "fdivl %2\n\t"
1227         "fmull %2\n\t"
1228         "fldl %1\n\t"
1229         "fsubp\n\t"
1230         "fistpl %0\n\t"
1231         : "=m" (ErrataPresent)
1232         : "m" (Value1),
1233           "m" (Value2)
1234     );
1235 #else
1236     __asm
1237     {
1238         fld Value1
1239         fdiv Value2
1240         fmul Value2
1241         fld Value1
1242         fsubp st(1), st(0)
1243         fistp ErrataPresent
1244     };
1245 #endif
1246 
1247     /* Restore CR0 */
1248     __writecr0(Cr0);
1249 
1250     /* Enable interrupts */
1251     _enable();
1252 
1253     /* Return if there's an errata */
1254     return ErrataPresent != 0;
1255 }
1256 
1257 VOID
1258 NTAPI
1259 KiFlushNPXState(IN PFLOATING_SAVE_AREA SaveArea)
1260 {
1261     ULONG EFlags, Cr0;
1262     PKTHREAD Thread, NpxThread;
1263     PFX_SAVE_AREA FxSaveArea;
1264 
1265     /* Save volatiles and disable interrupts */
1266     EFlags = __readeflags();
1267     _disable();
1268 
1269     /* Save the PCR and get the current thread */
1270     Thread = KeGetCurrentThread();
1271 
1272     /* Check if we're already loaded */
1273     if (Thread->NpxState != NPX_STATE_LOADED)
1274     {
1275         /* If there's nothing to load, quit */
1276         if (!SaveArea)
1277         {
1278             /* Restore interrupt state and return */
1279             __writeeflags(EFlags);
1280             return;
1281         }
1282 
1283         /* Need FXSR support for this */
1284         ASSERT(KeI386FxsrPresent == TRUE);
1285 
1286         /* Check for sane CR0 */
1287         Cr0 = __readcr0();
1288         if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
1289         {
1290             /* Mask out FPU flags */
1291             __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1292         }
1293 
1294         /* Get the NPX thread and check its FPU state */
1295         NpxThread = KeGetCurrentPrcb()->NpxThread;
1296         if ((NpxThread) && (NpxThread->NpxState == NPX_STATE_LOADED))
1297         {
1298             /* Get the FX frame and store the state there */
1299             FxSaveArea = KiGetThreadNpxArea(NpxThread);
1300             Ke386FxSave(FxSaveArea);
1301 
1302             /* NPX thread has lost its state */
1303             NpxThread->NpxState = NPX_STATE_NOT_LOADED;
1304         }
1305 
1306         /* Now load NPX state from the NPX area */
1307         FxSaveArea = KiGetThreadNpxArea(Thread);
1308         Ke386FxStore(FxSaveArea);
1309     }
1310     else
1311     {
1312         /* Check for sane CR0 */
1313         Cr0 = __readcr0();
1314         if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
1315         {
1316             /* Mask out FPU flags */
1317             __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1318         }
1319 
1320         /* Get FX frame */
1321         FxSaveArea = KiGetThreadNpxArea(Thread);
1322         Thread->NpxState = NPX_STATE_NOT_LOADED;
1323 
1324         /* Save state if supported by CPU */
1325         if (KeI386FxsrPresent) Ke386FxSave(FxSaveArea);
1326     }
1327 
1328     /* Now save the FN state wherever it was requested */
1329     if (SaveArea) Ke386FnSave(SaveArea);
1330 
1331     /* Clear NPX thread */
1332     KeGetCurrentPrcb()->NpxThread = NULL;
1333 
1334     /* Add the CR0 from the NPX frame */
1335     Cr0 |= NPX_STATE_NOT_LOADED;
1336     Cr0 |= FxSaveArea->Cr0NpxState;
1337     __writecr0(Cr0);
1338 
1339     /* Restore interrupt state */
1340     __writeeflags(EFlags);
1341 }
1342 
1343 /* PUBLIC FUNCTIONS **********************************************************/
1344 
1345 /*
1346  * @implemented
1347  */
1348 VOID
1349 NTAPI
1350 KiCoprocessorError(VOID)
1351 {
1352     PFX_SAVE_AREA NpxArea;
1353 
1354     /* Get the FPU area */
1355     NpxArea = KiGetThreadNpxArea(KeGetCurrentThread());
1356 
1357     /* Set CR0_TS */
1358     NpxArea->Cr0NpxState = CR0_TS;
1359     __writecr0(__readcr0() | CR0_TS);
1360 }
1361 
1362 /*
1363  * @implemented
1364  */
1365 NTSTATUS
1366 NTAPI
1367 KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)
1368 {
1369     PFNSAVE_FORMAT FpState;
1370     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
1371     DPRINT1("%s is not really implemented\n", __FUNCTION__);
1372 
1373     /* check if we are doing software emulation */
1374     if (!KeI386NpxPresent) return STATUS_ILLEGAL_FLOAT_CONTEXT;
1375 
1376     FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT));
1377     if (!FpState) return STATUS_INSUFFICIENT_RESOURCES;
1378 
1379     *((PVOID *) Save) = FpState;
1380 #ifdef __GNUC__
1381     asm volatile("fnsave %0\n\t" : "=m" (*FpState));
1382 #else
1383     __asm
1384     {
1385         mov eax, [FpState]
1386         fnsave [eax]
1387     };
1388 #endif
1389 
1390     KeGetCurrentThread()->Header.NpxIrql = KeGetCurrentIrql();
1391     return STATUS_SUCCESS;
1392 }
1393 
1394 /*
1395  * @implemented
1396  */
1397 NTSTATUS
1398 NTAPI
1399 KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save)
1400 {
1401     PFNSAVE_FORMAT FpState = *((PVOID *) Save);
1402     ASSERT(KeGetCurrentThread()->Header.NpxIrql == KeGetCurrentIrql());
1403     DPRINT1("%s is not really implemented\n", __FUNCTION__);
1404 
1405 #ifdef __GNUC__
1406     asm volatile("fnclex\n\t");
1407     asm volatile("frstor %0\n\t" : "=m" (*FpState));
1408 #else
1409     __asm
1410     {
1411         fnclex
1412         mov eax, [FpState]
1413         frstor [eax]
1414     };
1415 #endif
1416 
1417     ExFreePool(FpState);
1418     return STATUS_SUCCESS;
1419 }
1420 
1421 /*
1422  * @implemented
1423  */
1424 ULONG
1425 NTAPI
1426 KeGetRecommendedSharedDataAlignment(VOID)
1427 {
1428     /* Return the global variable */
1429     return KeLargestCacheLine;
1430 }
1431 
1432 VOID
1433 NTAPI
1434 KiFlushTargetEntireTb(IN PKIPI_CONTEXT PacketContext,
1435                       IN PVOID Ignored1,
1436                       IN PVOID Ignored2,
1437                       IN PVOID Ignored3)
1438 {
1439     /* Signal this packet as done */
1440     KiIpiSignalPacketDone(PacketContext);
1441 
1442     /* Flush the TB for the Current CPU */
1443     KeFlushCurrentTb();
1444 }
1445 
1446 /*
1447  * @implemented
1448  */
1449 VOID
1450 NTAPI
1451 KeFlushEntireTb(IN BOOLEAN Invalid,
1452                 IN BOOLEAN AllProcessors)
1453 {
1454     KIRQL OldIrql;
1455 #ifdef CONFIG_SMP
1456     KAFFINITY TargetAffinity;
1457     PKPRCB Prcb = KeGetCurrentPrcb();
1458 #endif
1459 
1460     /* Raise the IRQL for the TB Flush */
1461     OldIrql = KeRaiseIrqlToSynchLevel();
1462 
1463 #ifdef CONFIG_SMP
1464     /* FIXME: Use KiTbFlushTimeStamp to synchronize TB flush */
1465 
1466     /* Get the current processor affinity, and exclude ourselves */
1467     TargetAffinity = KeActiveProcessors;
1468     TargetAffinity &= ~Prcb->SetMember;
1469 
1470     /* Make sure this is MP */
1471     if (TargetAffinity)
1472     {
1473         /* Send an IPI TB flush to the other processors */
1474         KiIpiSendPacket(TargetAffinity,
1475                         KiFlushTargetEntireTb,
1476                         NULL,
1477                         0,
1478                         NULL);
1479     }
1480 #endif
1481 
1482     /* Flush the TB for the Current CPU, and update the flush stamp */
1483     KeFlushCurrentTb();
1484 
1485 #ifdef CONFIG_SMP
1486     /* If this is MP, wait for the other processors to finish */
1487     if (TargetAffinity)
1488     {
1489         /* Sanity check */
1490         ASSERT(Prcb == KeGetCurrentPrcb());
1491 
1492         /* FIXME: TODO */
1493         ASSERTMSG("Not yet implemented\n", FALSE);
1494     }
1495 #endif
1496 
1497     /* Update the flush stamp and return to original IRQL */
1498     InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1);
1499     KeLowerIrql(OldIrql);
1500 }
1501 
1502 /*
1503  * @implemented
1504  */
1505 VOID
1506 NTAPI
1507 KeSetDmaIoCoherency(IN ULONG Coherency)
1508 {
1509     /* Save the coherency globally */
1510     KiDmaIoCoherency = Coherency;
1511 }
1512 
1513 /*
1514  * @implemented
1515  */
1516 KAFFINITY
1517 NTAPI
1518 KeQueryActiveProcessors(VOID)
1519 {
1520     PAGED_CODE();
1521 
1522     /* Simply return the number of active processors */
1523     return KeActiveProcessors;
1524 }
1525 
1526 /*
1527  * @implemented
1528  */
1529 VOID
1530 __cdecl
1531 KeSaveStateForHibernate(IN PKPROCESSOR_STATE State)
1532 {
1533     /* Capture the context */
1534     RtlCaptureContext(&State->ContextFrame);
1535 
1536     /* Capture the control state */
1537     KiSaveProcessorControlState(State);
1538 }
1539