bcac032e7777d17efa62af858fef1b7247b7e5bf
[reactos.git] / reactos / ntoskrnl / ke / i386 / cpu.c
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 VOID
89 NTAPI
90 INIT_FUNCTION
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 ULONG
156 NTAPI
157 INIT_FUNCTION
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 ULONG
212 NTAPI
213 INIT_FUNCTION
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 VOID
458 NTAPI
459 INIT_FUNCTION
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 /*
529 * Valid values are from 0x40 (0 bytes) to 0x49
530 * (32MB), or from 0x80 to 0x89 (same size but
531 * 8-way associative.
532 */
533 if (((RegisterByte > 0x40) && (RegisterByte <= 0x47)) ||
534 ((RegisterByte > 0x78) && (RegisterByte <= 0x7C)) ||
535 ((RegisterByte > 0x80) && (RegisterByte <= 0x85)))
536 {
537 /* Compute associativity */
538 Associativity = 4;
539 if (RegisterByte >= 0x79) Associativity = 8;
540
541 /* Mask out only the first nibble */
542 RegisterByte &= 0x07;
543
544 /* Check if this cache is bigger than the last */
545 Size = 0x10000 << RegisterByte;
546 if ((Size / Associativity) > CurrentSize)
547 {
548 /* Set the L2 Cache Size and Associativity */
549 CurrentSize = Size / Associativity;
550 Pcr->SecondLevelCacheSize = Size;
551 Pcr->SecondLevelCacheAssociativity = Associativity;
552 }
553 }
554 else if ((RegisterByte > 0x21) && (RegisterByte <= 0x29))
555 {
556 /* Set minimum cache line size */
557 if (CacheLine < 128) CacheLine = 128;
558
559 /* Hard-code size/associativity */
560 Associativity = 8;
561 switch (RegisterByte)
562 {
563 case 0x22:
564 Size = 512 * 1024;
565 Associativity = 4;
566 break;
567
568 case 0x23:
569 Size = 1024 * 1024;
570 break;
571
572 case 0x25:
573 Size = 2048 * 1024;
574 break;
575
576 case 0x29:
577 Size = 4096 * 1024;
578 break;
579
580 default:
581 Size = 0;
582 break;
583 }
584
585 /* Check if this cache is bigger than the last */
586 if ((Size / Associativity) > CurrentSize)
587 {
588 /* Set the L2 Cache Size and Associativity */
589 CurrentSize = Size / Associativity;
590 Pcr->SecondLevelCacheSize = Size;
591 Pcr->SecondLevelCacheAssociativity = Associativity;
592 }
593 }
594 else if (((RegisterByte > 0x65) && (RegisterByte < 0x69)) ||
595 (RegisterByte == 0x2C) || (RegisterByte == 0xF0))
596 {
597 /* Indicates L1 cache line of 64 bytes */
598 KePrefetchNTAGranularity = 64;
599 }
600 else if (RegisterByte == 0xF1)
601 {
602 /* Indicates L1 cache line of 128 bytes */
603 KePrefetchNTAGranularity = 128;
604 }
605 else if (((RegisterByte >= 0x4A) && (RegisterByte <= 0x4C)) ||
606 (RegisterByte == 0x78) ||
607 (RegisterByte == 0x7D) ||
608 (RegisterByte == 0x7F) ||
609 (RegisterByte == 0x86) ||
610 (RegisterByte == 0x87))
611 {
612 /* Set minimum cache line size */
613 if (CacheLine < 64) CacheLine = 64;
614
615 /* Hard-code size/associativity */
616 switch (RegisterByte)
617 {
618 case 0x4A:
619 Size = 4 * 1024 * 1024;
620 Associativity = 8;
621 break;
622
623 case 0x4B:
624 Size = 6 * 1024 * 1024;
625 Associativity = 12;
626 break;
627
628 case 0x4C:
629 Size = 8 * 1024 * 1024;
630 Associativity = 16;
631 break;
632
633 case 0x78:
634 Size = 1 * 1024 * 1024;
635 Associativity = 4;
636 break;
637
638 case 0x7D:
639 Size = 2 * 1024 * 1024;
640 Associativity = 8;
641 break;
642
643 case 0x7F:
644 Size = 512 * 1024;
645 Associativity = 2;
646 break;
647
648 case 0x86:
649 Size = 512 * 1024;
650 Associativity = 4;
651 break;
652
653 case 0x87:
654 Size = 1 * 1024 * 1024;
655 Associativity = 8;
656 break;
657
658 default:
659 Size = 0;
660 break;
661 }
662
663 /* Check if this cache is bigger than the last */
664 if ((Size / Associativity) > CurrentSize)
665 {
666 /* Set the L2 Cache Size and Associativity */
667 CurrentSize = Size / Associativity;
668 Pcr->SecondLevelCacheSize = Size;
669 Pcr->SecondLevelCacheAssociativity = Associativity;
670 }
671 }
672 }
673 }
674 } while (--CacheRequests);
675 }
676 break;
677
678 case CPU_AMD:
679
680 /* Check if we support CPUID 0x80000005 */
681 KiCpuId(&CpuInfo, 0x80000000);
682 if (CpuInfo.Eax >= 0x80000006)
683 {
684 /* Get L1 size first */
685 KiCpuId(&CpuInfo, 0x80000005);
686 KePrefetchNTAGranularity = CpuInfo.Ecx & 0xFF;
687
688 /* Check if we support CPUID 0x80000006 */
689 KiCpuId(&CpuInfo, 0x80000000);
690 if (CpuInfo.Eax >= 0x80000006)
691 {
692 /* Get 2nd level cache and tlb size */
693 KiCpuId(&CpuInfo, 0x80000006);
694
695 /* Cache line size */
696 CacheLine = CpuInfo.Ecx & 0xFF;
697
698 /* Hardcode associativity */
699 RegisterByte = (CpuInfo.Ecx >> 12) & 0xFF;
700 switch (RegisterByte)
701 {
702 case 2:
703 Associativity = 2;
704 break;
705
706 case 4:
707 Associativity = 4;
708 break;
709
710 case 6:
711 Associativity = 8;
712 break;
713
714 case 8:
715 case 15:
716 Associativity = 16;
717 break;
718
719 default:
720 Associativity = 1;
721 break;
722 }
723
724 /* Compute size */
725 Size = (CpuInfo.Ecx >> 16) << 10;
726
727 /* Hack for Model 6, Steping 300 */
728 if ((KeGetCurrentPrcb()->CpuType == 6) &&
729 (KeGetCurrentPrcb()->CpuStep == 0x300))
730 {
731 /* Stick 64K in there */
732 Size = 64 * 1024;
733 }
734
735 /* Set the L2 Cache Size and associativity */
736 Pcr->SecondLevelCacheSize = Size;
737 Pcr->SecondLevelCacheAssociativity = Associativity;
738 }
739 }
740 break;
741
742 case CPU_CYRIX:
743 case CPU_TRANSMETA:
744 case CPU_CENTAUR:
745 case CPU_RISE:
746
747 /* FIXME */
748 break;
749 }
750
751 /* Set the cache line */
752 if (CacheLine > KeLargestCacheLine) KeLargestCacheLine = CacheLine;
753 DPRINT1("Prefetch Cache: %lu bytes\tL2 Cache: %lu bytes\tL2 Cache Line: %lu bytes\tL2 Cache Associativity: %lu\n",
754 KePrefetchNTAGranularity,
755 Pcr->SecondLevelCacheSize,
756 KeLargestCacheLine,
757 Pcr->SecondLevelCacheAssociativity);
758 }
759
760 VOID
761 NTAPI
762 INIT_FUNCTION
763 KiSetCR0Bits(VOID)
764 {
765 ULONG Cr0;
766
767 /* Save current CR0 */
768 Cr0 = __readcr0();
769
770 /* If this is a 486, enable Write-Protection */
771 if (KeGetCurrentPrcb()->CpuType > 3) Cr0 |= CR0_WP;
772
773 /* Set new Cr0 */
774 __writecr0(Cr0);
775 }
776
777 VOID
778 NTAPI
779 INIT_FUNCTION
780 KiInitializeTSS2(IN PKTSS Tss,
781 IN PKGDTENTRY TssEntry OPTIONAL)
782 {
783 PUCHAR p;
784
785 /* Make sure the GDT Entry is valid */
786 if (TssEntry)
787 {
788 /* Set the Limit */
789 TssEntry->LimitLow = sizeof(KTSS) - 1;
790 TssEntry->HighWord.Bits.LimitHi = 0;
791 }
792
793 /* Now clear the I/O Map */
794 ASSERT(IOPM_COUNT == 1);
795 RtlFillMemory(Tss->IoMaps[0].IoMap, IOPM_FULL_SIZE, 0xFF);
796
797 /* Initialize Interrupt Direction Maps */
798 p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);
799 RtlZeroMemory(p, IOPM_DIRECTION_MAP_SIZE);
800
801 /* Add DPMI support for interrupts */
802 p[0] = 4;
803 p[3] = 0x18;
804 p[4] = 0x18;
805
806 /* Initialize the default Interrupt Direction Map */
807 p = Tss->IntDirectionMap;
808 RtlZeroMemory(Tss->IntDirectionMap, IOPM_DIRECTION_MAP_SIZE);
809
810 /* Add DPMI support */
811 p[0] = 4;
812 p[3] = 0x18;
813 p[4] = 0x18;
814 }
815
816 VOID
817 NTAPI
818 KiInitializeTSS(IN PKTSS Tss)
819 {
820 /* Set an invalid map base */
821 Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
822
823 /* Disable traps during Task Switches */
824 Tss->Flags = 0;
825
826 /* Set LDT and Ring 0 SS */
827 Tss->LDT = 0;
828 Tss->Ss0 = KGDT_R0_DATA;
829 }
830
831 VOID
832 FASTCALL
833 INIT_FUNCTION
834 Ki386InitializeTss(IN PKTSS Tss,
835 IN PKIDTENTRY Idt,
836 IN PKGDTENTRY Gdt)
837 {
838 PKGDTENTRY TssEntry, TaskGateEntry;
839
840 /* Initialize the boot TSS. */
841 TssEntry = &Gdt[KGDT_TSS / sizeof(KGDTENTRY)];
842 TssEntry->HighWord.Bits.Type = I386_TSS;
843 TssEntry->HighWord.Bits.Pres = 1;
844 TssEntry->HighWord.Bits.Dpl = 0;
845 KiInitializeTSS2(Tss, TssEntry);
846 KiInitializeTSS(Tss);
847
848 /* Load the task register */
849 Ke386SetTr(KGDT_TSS);
850
851 /* Setup the Task Gate for Double Fault Traps */
852 TaskGateEntry = (PKGDTENTRY)&Idt[8];
853 TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
854 TaskGateEntry->HighWord.Bits.Pres = 1;
855 TaskGateEntry->HighWord.Bits.Dpl = 0;
856 ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_DF_TSS;
857
858 /* Initialize the TSS used for handling double faults. */
859 Tss = (PKTSS)KiDoubleFaultTSS;
860 KiInitializeTSS(Tss);
861 Tss->CR3 = __readcr3();
862 Tss->Esp0 = KiDoubleFaultStack;
863 Tss->Esp = KiDoubleFaultStack;
864 Tss->Eip = PtrToUlong(KiTrap08);
865 Tss->Cs = KGDT_R0_CODE;
866 Tss->Fs = KGDT_R0_PCR;
867 Tss->Ss = Ke386GetSs();
868 Tss->Es = KGDT_R3_DATA | RPL_MASK;
869 Tss->Ds = KGDT_R3_DATA | RPL_MASK;
870
871 /* Setup the Double Trap TSS entry in the GDT */
872 TssEntry = &Gdt[KGDT_DF_TSS / sizeof(KGDTENTRY)];
873 TssEntry->HighWord.Bits.Type = I386_TSS;
874 TssEntry->HighWord.Bits.Pres = 1;
875 TssEntry->HighWord.Bits.Dpl = 0;
876 TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
877 TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
878 TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
879 TssEntry->LimitLow = KTSS_IO_MAPS;
880
881 /* Now setup the NMI Task Gate */
882 TaskGateEntry = (PKGDTENTRY)&Idt[2];
883 TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
884 TaskGateEntry->HighWord.Bits.Pres = 1;
885 TaskGateEntry->HighWord.Bits.Dpl = 0;
886 ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_NMI_TSS;
887
888 /* Initialize the actual TSS */
889 Tss = (PKTSS)KiNMITSS;
890 KiInitializeTSS(Tss);
891 Tss->CR3 = __readcr3();
892 Tss->Esp0 = KiDoubleFaultStack;
893 Tss->Esp = KiDoubleFaultStack;
894 Tss->Eip = PtrToUlong(KiTrap02);
895 Tss->Cs = KGDT_R0_CODE;
896 Tss->Fs = KGDT_R0_PCR;
897 Tss->Ss = Ke386GetSs();
898 Tss->Es = KGDT_R3_DATA | RPL_MASK;
899 Tss->Ds = KGDT_R3_DATA | RPL_MASK;
900
901 /* And its associated TSS Entry */
902 TssEntry = &Gdt[KGDT_NMI_TSS / sizeof(KGDTENTRY)];
903 TssEntry->HighWord.Bits.Type = I386_TSS;
904 TssEntry->HighWord.Bits.Pres = 1;
905 TssEntry->HighWord.Bits.Dpl = 0;
906 TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
907 TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
908 TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
909 TssEntry->LimitLow = KTSS_IO_MAPS;
910 }
911
912 VOID
913 NTAPI
914 KeFlushCurrentTb(VOID)
915 {
916 /* Flush the TLB by resetting CR3 */
917 __writecr3(__readcr3());
918 }
919
920 VOID
921 NTAPI
922 KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState)
923 {
924 PKGDTENTRY TssEntry;
925
926 //
927 // Restore the CR registers
928 //
929 __writecr0(ProcessorState->SpecialRegisters.Cr0);
930 Ke386SetCr2(ProcessorState->SpecialRegisters.Cr2);
931 __writecr3(ProcessorState->SpecialRegisters.Cr3);
932 if (KeFeatureBits & KF_CR4) __writecr4(ProcessorState->SpecialRegisters.Cr4);
933
934 //
935 // Restore the DR registers
936 //
937 __writedr(0, ProcessorState->SpecialRegisters.KernelDr0);
938 __writedr(1, ProcessorState->SpecialRegisters.KernelDr1);
939 __writedr(2, ProcessorState->SpecialRegisters.KernelDr2);
940 __writedr(3, ProcessorState->SpecialRegisters.KernelDr3);
941 __writedr(6, ProcessorState->SpecialRegisters.KernelDr6);
942 __writedr(7, ProcessorState->SpecialRegisters.KernelDr7);
943
944 //
945 // Restore GDT and IDT
946 //
947 Ke386SetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
948 __lidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
949
950 //
951 // Clear the busy flag so we don't crash if we reload the same selector
952 //
953 TssEntry = (PKGDTENTRY)(ProcessorState->SpecialRegisters.Gdtr.Base +
954 ProcessorState->SpecialRegisters.Tr);
955 TssEntry->HighWord.Bytes.Flags1 &= ~0x2;
956
957 //
958 // Restore TSS and LDT
959 //
960 Ke386SetTr(ProcessorState->SpecialRegisters.Tr);
961 Ke386SetLocalDescriptorTable(ProcessorState->SpecialRegisters.Ldtr);
962 }
963
964 VOID
965 NTAPI
966 KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState)
967 {
968 /* Save the CR registers */
969 ProcessorState->SpecialRegisters.Cr0 = __readcr0();
970 ProcessorState->SpecialRegisters.Cr2 = __readcr2();
971 ProcessorState->SpecialRegisters.Cr3 = __readcr3();
972 ProcessorState->SpecialRegisters.Cr4 = (KeFeatureBits & KF_CR4) ?
973 __readcr4() : 0;
974
975 /* Save the DR registers */
976 ProcessorState->SpecialRegisters.KernelDr0 = __readdr(0);
977 ProcessorState->SpecialRegisters.KernelDr1 = __readdr(1);
978 ProcessorState->SpecialRegisters.KernelDr2 = __readdr(2);
979 ProcessorState->SpecialRegisters.KernelDr3 = __readdr(3);
980 ProcessorState->SpecialRegisters.KernelDr6 = __readdr(6);
981 ProcessorState->SpecialRegisters.KernelDr7 = __readdr(7);
982 __writedr(7, 0);
983
984 /* Save GDT, IDT, LDT and TSS */
985 Ke386GetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
986 __sidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
987 ProcessorState->SpecialRegisters.Tr = Ke386GetTr();
988 ProcessorState->SpecialRegisters.Ldtr = Ke386GetLocalDescriptorTable();
989 }
990
991 VOID
992 NTAPI
993 INIT_FUNCTION
994 KiInitializeMachineType(VOID)
995 {
996 /* Set the Machine Type we got from NTLDR */
997 KeI386MachineType = KeLoaderBlock->u.I386.MachineType & 0x000FF;
998 }
999
1000 ULONG_PTR
1001 NTAPI
1002 INIT_FUNCTION
1003 KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
1004 {
1005 /* Set CS and ESP */
1006 __writemsr(0x174, KGDT_R0_CODE);
1007 __writemsr(0x175, (ULONG_PTR)KeGetCurrentPrcb()->DpcStack);
1008
1009 /* Set LSTAR */
1010 __writemsr(0x176, (ULONG_PTR)KiFastCallEntry);
1011 return 0;
1012 }
1013
1014 VOID
1015 NTAPI
1016 INIT_FUNCTION
1017 KiRestoreFastSyscallReturnState(VOID)
1018 {
1019 /* Check if the CPU Supports fast system call */
1020 if (KeFeatureBits & KF_FAST_SYSCALL)
1021 {
1022 /* Check if it has been disabled */
1023 if (KiFastSystemCallDisable)
1024 {
1025 /* Disable fast system call */
1026 KeFeatureBits &= ~KF_FAST_SYSCALL;
1027 KiFastCallExitHandler = KiSystemCallTrapReturn;
1028 DPRINT1("Support for SYSENTER disabled.\n");
1029 }
1030 else
1031 {
1032 /* Do an IPI to enable it */
1033 KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters, 0);
1034
1035 /* It's enabled, so use the proper exit stub */
1036 KiFastCallExitHandler = KiSystemCallSysExitReturn;
1037 DPRINT("Support for SYSENTER detected.\n");
1038 }
1039 }
1040 else
1041 {
1042 /* Use the IRET handler */
1043 KiFastCallExitHandler = KiSystemCallTrapReturn;
1044 DPRINT1("No support for SYSENTER detected.\n");
1045 }
1046 }
1047
1048 ULONG_PTR
1049 NTAPI
1050 INIT_FUNCTION
1051 Ki386EnableDE(IN ULONG_PTR Context)
1052 {
1053 /* Enable DE */
1054 __writecr4(__readcr4() | CR4_DE);
1055 return 0;
1056 }
1057
1058 ULONG_PTR
1059 NTAPI
1060 INIT_FUNCTION
1061 Ki386EnableFxsr(IN ULONG_PTR Context)
1062 {
1063 /* Enable FXSR */
1064 __writecr4(__readcr4() | CR4_FXSR);
1065 return 0;
1066 }
1067
1068 ULONG_PTR
1069 NTAPI
1070 INIT_FUNCTION
1071 Ki386EnableXMMIExceptions(IN ULONG_PTR Context)
1072 {
1073 PKIDTENTRY IdtEntry;
1074
1075 /* Get the IDT Entry for Interrupt 0x13 */
1076 IdtEntry = &((PKIPCR)KeGetPcr())->IDT[0x13];
1077
1078 /* Set it up */
1079 IdtEntry->Selector = KGDT_R0_CODE;
1080 IdtEntry->Offset = ((ULONG_PTR)KiTrap13 & 0xFFFF);
1081 IdtEntry->ExtendedOffset = ((ULONG_PTR)KiTrap13 >> 16) & 0xFFFF;
1082 ((PKIDT_ACCESS)&IdtEntry->Access)->Dpl = 0;
1083 ((PKIDT_ACCESS)&IdtEntry->Access)->Present = 1;
1084 ((PKIDT_ACCESS)&IdtEntry->Access)->SegmentType = I386_INTERRUPT_GATE;
1085
1086 /* Enable XMMI exceptions */
1087 __writecr4(__readcr4() | CR4_XMMEXCPT);
1088 return 0;
1089 }
1090
1091 VOID
1092 NTAPI
1093 INIT_FUNCTION
1094 KiI386PentiumLockErrataFixup(VOID)
1095 {
1096 KDESCRIPTOR IdtDescriptor = {0, 0, 0};
1097 PKIDTENTRY NewIdt, NewIdt2;
1098
1099 /* Allocate memory for a new IDT */
1100 NewIdt = ExAllocatePool(NonPagedPool, 2 * PAGE_SIZE);
1101
1102 /* Put everything after the first 7 entries on a new page */
1103 NewIdt2 = (PVOID)((ULONG_PTR)NewIdt + PAGE_SIZE - (7 * sizeof(KIDTENTRY)));
1104
1105 /* Disable interrupts */
1106 _disable();
1107
1108 /* Get the current IDT and copy it */
1109 __sidt(&IdtDescriptor.Limit);
1110 RtlCopyMemory(NewIdt2,
1111 (PVOID)IdtDescriptor.Base,
1112 IdtDescriptor.Limit + 1);
1113 IdtDescriptor.Base = (ULONG)NewIdt2;
1114
1115 /* Set the new IDT */
1116 __lidt(&IdtDescriptor.Limit);
1117 ((PKIPCR)KeGetPcr())->IDT = NewIdt2;
1118
1119 /* Restore interrupts */
1120 _enable();
1121
1122 /* Set the first 7 entries as read-only to produce a fault */
1123 MmSetPageProtect(NULL, NewIdt, PAGE_READONLY);
1124 }
1125
1126 BOOLEAN
1127 NTAPI
1128 KeInvalidateAllCaches(VOID)
1129 {
1130 /* Only supported on Pentium Pro and higher */
1131 if (KeI386CpuType < 6) return FALSE;
1132
1133 /* Invalidate all caches */
1134 __wbinvd();
1135 return TRUE;
1136 }
1137
1138 VOID
1139 FASTCALL
1140 KeZeroPages(IN PVOID Address,
1141 IN ULONG Size)
1142 {
1143 /* Not using XMMI in this routine */
1144 RtlZeroMemory(Address, Size);
1145 }
1146
1147 VOID
1148 NTAPI
1149 KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame,
1150 IN PKEXCEPTION_FRAME ExceptionFrame)
1151 {
1152 PKPRCB Prcb = KeGetCurrentPrcb();
1153
1154 //
1155 // Save full context
1156 //
1157 Prcb->ProcessorState.ContextFrame.ContextFlags = CONTEXT_FULL |
1158 CONTEXT_DEBUG_REGISTERS;
1159 KeTrapFrameToContext(TrapFrame, NULL, &Prcb->ProcessorState.ContextFrame);
1160
1161 //
1162 // Save control registers
1163 //
1164 KiSaveProcessorControlState(&Prcb->ProcessorState);
1165 }
1166
1167 BOOLEAN
1168 NTAPI
1169 INIT_FUNCTION
1170 KiIsNpxPresent(VOID)
1171 {
1172 ULONG Cr0;
1173 USHORT Magic;
1174
1175 /* Set magic */
1176 Magic = 0xFFFF;
1177
1178 /* Read CR0 and mask out FPU flags */
1179 Cr0 = __readcr0() & ~(CR0_MP | CR0_TS | CR0_EM | CR0_ET);
1180
1181 /* Store on FPU stack */
1182 #ifdef _MSC_VER
1183 __asm fninit;
1184 __asm fnstsw Magic;
1185 #else
1186 asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic));
1187 #endif
1188
1189 /* Magic should now be cleared */
1190 if (Magic & 0xFF)
1191 {
1192 /* You don't have an FPU -- enable emulation for now */
1193 __writecr0(Cr0 | CR0_EM | CR0_TS);
1194 return FALSE;
1195 }
1196
1197 /* You have an FPU, enable it */
1198 Cr0 |= CR0_ET;
1199
1200 /* Enable INT 16 on 486 and higher */
1201 if (KeGetCurrentPrcb()->CpuType >= 3) Cr0 |= CR0_NE;
1202
1203 /* Set FPU state */
1204 __writecr0(Cr0 | CR0_EM | CR0_TS);
1205 return TRUE;
1206 }
1207
1208 BOOLEAN
1209 NTAPI
1210 INIT_FUNCTION
1211 KiIsNpxErrataPresent(VOID)
1212 {
1213 static double Value1 = 4195835.0, Value2 = 3145727.0;
1214 INT ErrataPresent;
1215 ULONG Cr0;
1216
1217 /* Disable interrupts */
1218 _disable();
1219
1220 /* Read CR0 and remove FPU flags */
1221 Cr0 = __readcr0();
1222 __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1223
1224 /* Initialize FPU state */
1225 Ke386FnInit();
1226
1227 /* Multiply the magic values and divide, we should get the result back */
1228 #ifdef __GNUC__
1229 __asm__ __volatile__
1230 (
1231 "fldl %1\n\t"
1232 "fdivl %2\n\t"
1233 "fmull %2\n\t"
1234 "fldl %1\n\t"
1235 "fsubp\n\t"
1236 "fistpl %0\n\t"
1237 : "=m" (ErrataPresent)
1238 : "m" (Value1),
1239 "m" (Value2)
1240 );
1241 #else
1242 __asm
1243 {
1244 fld Value1
1245 fdiv Value2
1246 fmul Value2
1247 fld Value1
1248 fsubp st(1), st(0)
1249 fistp ErrataPresent
1250 };
1251 #endif
1252
1253 /* Restore CR0 */
1254 __writecr0(Cr0);
1255
1256 /* Enable interrupts */
1257 _enable();
1258
1259 /* Return if there's an errata */
1260 return ErrataPresent != 0;
1261 }
1262
1263 VOID
1264 NTAPI
1265 KiFlushNPXState(IN PFLOATING_SAVE_AREA SaveArea)
1266 {
1267 ULONG EFlags, Cr0;
1268 PKTHREAD Thread, NpxThread;
1269 PFX_SAVE_AREA FxSaveArea;
1270
1271 /* Save volatiles and disable interrupts */
1272 EFlags = __readeflags();
1273 _disable();
1274
1275 /* Save the PCR and get the current thread */
1276 Thread = KeGetCurrentThread();
1277
1278 /* Check if we're already loaded */
1279 if (Thread->NpxState != NPX_STATE_LOADED)
1280 {
1281 /* If there's nothing to load, quit */
1282 if (!SaveArea)
1283 {
1284 /* Restore interrupt state and return */
1285 __writeeflags(EFlags);
1286 return;
1287 }
1288
1289 /* Need FXSR support for this */
1290 ASSERT(KeI386FxsrPresent == TRUE);
1291
1292 /* Check for sane CR0 */
1293 Cr0 = __readcr0();
1294 if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
1295 {
1296 /* Mask out FPU flags */
1297 __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1298 }
1299
1300 /* Get the NPX thread and check its FPU state */
1301 NpxThread = KeGetCurrentPrcb()->NpxThread;
1302 if ((NpxThread) && (NpxThread->NpxState == NPX_STATE_LOADED))
1303 {
1304 /* Get the FX frame and store the state there */
1305 FxSaveArea = KiGetThreadNpxArea(NpxThread);
1306 Ke386FxSave(FxSaveArea);
1307
1308 /* NPX thread has lost its state */
1309 NpxThread->NpxState = NPX_STATE_NOT_LOADED;
1310 }
1311
1312 /* Now load NPX state from the NPX area */
1313 FxSaveArea = KiGetThreadNpxArea(Thread);
1314 Ke386FxStore(FxSaveArea);
1315 }
1316 else
1317 {
1318 /* Check for sane CR0 */
1319 Cr0 = __readcr0();
1320 if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
1321 {
1322 /* Mask out FPU flags */
1323 __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1324 }
1325
1326 /* Get FX frame */
1327 FxSaveArea = KiGetThreadNpxArea(Thread);
1328 Thread->NpxState = NPX_STATE_NOT_LOADED;
1329
1330 /* Save state if supported by CPU */
1331 if (KeI386FxsrPresent) Ke386FxSave(FxSaveArea);
1332 }
1333
1334 /* Now save the FN state wherever it was requested */
1335 if (SaveArea) Ke386FnSave(SaveArea);
1336
1337 /* Clear NPX thread */
1338 KeGetCurrentPrcb()->NpxThread = NULL;
1339
1340 /* Add the CR0 from the NPX frame */
1341 Cr0 |= NPX_STATE_NOT_LOADED;
1342 Cr0 |= FxSaveArea->Cr0NpxState;
1343 __writecr0(Cr0);
1344
1345 /* Restore interrupt state */
1346 __writeeflags(EFlags);
1347 }
1348
1349 /* PUBLIC FUNCTIONS **********************************************************/
1350
1351 /*
1352 * @implemented
1353 */
1354 VOID
1355 NTAPI
1356 KiCoprocessorError(VOID)
1357 {
1358 PFX_SAVE_AREA NpxArea;
1359
1360 /* Get the FPU area */
1361 NpxArea = KiGetThreadNpxArea(KeGetCurrentThread());
1362
1363 /* Set CR0_TS */
1364 NpxArea->Cr0NpxState = CR0_TS;
1365 __writecr0(__readcr0() | CR0_TS);
1366 }
1367
1368 /*
1369 * @implemented
1370 */
1371 NTSTATUS
1372 NTAPI
1373 KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)
1374 {
1375 PFNSAVE_FORMAT FpState;
1376 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
1377 DPRINT1("%s is not really implemented\n", __FUNCTION__);
1378
1379 /* check if we are doing software emulation */
1380 if (!KeI386NpxPresent) return STATUS_ILLEGAL_FLOAT_CONTEXT;
1381
1382 FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT));
1383 if (!FpState) return STATUS_INSUFFICIENT_RESOURCES;
1384
1385 *((PVOID *) Save) = FpState;
1386 #ifdef __GNUC__
1387 asm volatile("fnsave %0\n\t" : "=m" (*FpState));
1388 #else
1389 __asm
1390 {
1391 mov eax, [FpState]
1392 fnsave [eax]
1393 };
1394 #endif
1395
1396 KeGetCurrentThread()->Header.NpxIrql = KeGetCurrentIrql();
1397 return STATUS_SUCCESS;
1398 }
1399
1400 /*
1401 * @implemented
1402 */
1403 NTSTATUS
1404 NTAPI
1405 KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save)
1406 {
1407 PFNSAVE_FORMAT FpState = *((PVOID *) Save);
1408 ASSERT(KeGetCurrentThread()->Header.NpxIrql == KeGetCurrentIrql());
1409 DPRINT1("%s is not really implemented\n", __FUNCTION__);
1410
1411 #ifdef __GNUC__
1412 asm volatile("fnclex\n\t");
1413 asm volatile("frstor %0\n\t" : "=m" (*FpState));
1414 #else
1415 __asm
1416 {
1417 fnclex
1418 mov eax, [FpState]
1419 frstor [eax]
1420 };
1421 #endif
1422
1423 ExFreePool(FpState);
1424 return STATUS_SUCCESS;
1425 }
1426
1427 /*
1428 * @implemented
1429 */
1430 ULONG
1431 NTAPI
1432 KeGetRecommendedSharedDataAlignment(VOID)
1433 {
1434 /* Return the global variable */
1435 return KeLargestCacheLine;
1436 }
1437
1438 VOID
1439 NTAPI
1440 KiFlushTargetEntireTb(IN PKIPI_CONTEXT PacketContext,
1441 IN PVOID Ignored1,
1442 IN PVOID Ignored2,
1443 IN PVOID Ignored3)
1444 {
1445 /* Signal this packet as done */
1446 KiIpiSignalPacketDone(PacketContext);
1447
1448 /* Flush the TB for the Current CPU */
1449 KeFlushCurrentTb();
1450 }
1451
1452 /*
1453 * @implemented
1454 */
1455 VOID
1456 NTAPI
1457 KeFlushEntireTb(IN BOOLEAN Invalid,
1458 IN BOOLEAN AllProcessors)
1459 {
1460 KIRQL OldIrql;
1461 #ifdef CONFIG_SMP
1462 KAFFINITY TargetAffinity;
1463 PKPRCB Prcb = KeGetCurrentPrcb();
1464 #endif
1465
1466 /* Raise the IRQL for the TB Flush */
1467 OldIrql = KeRaiseIrqlToSynchLevel();
1468
1469 #ifdef CONFIG_SMP
1470 /* FIXME: Use KiTbFlushTimeStamp to synchronize TB flush */
1471
1472 /* Get the current processor affinity, and exclude ourselves */
1473 TargetAffinity = KeActiveProcessors;
1474 TargetAffinity &= ~Prcb->SetMember;
1475
1476 /* Make sure this is MP */
1477 if (TargetAffinity)
1478 {
1479 /* Send an IPI TB flush to the other processors */
1480 KiIpiSendPacket(TargetAffinity,
1481 KiFlushTargetEntireTb,
1482 NULL,
1483 0,
1484 NULL);
1485 }
1486 #endif
1487
1488 /* Flush the TB for the Current CPU, and update the flush stamp */
1489 KeFlushCurrentTb();
1490
1491 #ifdef CONFIG_SMP
1492 /* If this is MP, wait for the other processors to finish */
1493 if (TargetAffinity)
1494 {
1495 /* Sanity check */
1496 ASSERT(Prcb == KeGetCurrentPrcb());
1497
1498 /* FIXME: TODO */
1499 ASSERTMSG("Not yet implemented\n", FALSE);
1500 }
1501 #endif
1502
1503 /* Update the flush stamp and return to original IRQL */
1504 InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1);
1505 KeLowerIrql(OldIrql);
1506 }
1507
1508 /*
1509 * @implemented
1510 */
1511 VOID
1512 NTAPI
1513 KeSetDmaIoCoherency(IN ULONG Coherency)
1514 {
1515 /* Save the coherency globally */
1516 KiDmaIoCoherency = Coherency;
1517 }
1518
1519 /*
1520 * @implemented
1521 */
1522 KAFFINITY
1523 NTAPI
1524 KeQueryActiveProcessors(VOID)
1525 {
1526 PAGED_CODE();
1527
1528 /* Simply return the number of active processors */
1529 return KeActiveProcessors;
1530 }
1531
1532 /*
1533 * @implemented
1534 */
1535 VOID
1536 __cdecl
1537 KeSaveStateForHibernate(IN PKPROCESSOR_STATE State)
1538 {
1539 /* Capture the context */
1540 RtlCaptureContext(&State->ContextFrame);
1541
1542 /* Capture the control state */
1543 KiSaveProcessorControlState(State);
1544 }