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