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)
9 /* INCLUDES *****************************************************************/
15 /* GLOBALS *******************************************************************/
17 /* The TSS to use for Double Fault Traps (INT 0x9) */
18 UCHAR KiDoubleFaultTSS
[KTSS_IO_MAPS
];
20 /* The TSS to use for NMI Fault Traps (INT 0x2) */
21 UCHAR KiNMITSS
[KTSS_IO_MAPS
];
23 /* CPU Features and Flags */
26 ULONG KeProcessorArchitecture
;
27 ULONG KeProcessorLevel
;
28 ULONG KeProcessorRevision
;
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
;
49 /* The distance between SYSEXIT and IRETD return modes */
50 UCHAR KiSystemCallExitAdjust
;
52 /* The offset that was applied -- either 0 or the value above */
53 UCHAR KiSystemCallExitAdjusted
;
55 /* Whether the adjustment was already done once */
56 BOOLEAN KiFastCallCopyDoneOnce
;
59 volatile LONG KiTbFlushTimeStamp
;
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";
69 /* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/
73 CPUID(IN ULONG InfoType
,
74 OUT PULONG CpuInfoEax
,
75 OUT PULONG CpuInfoEbx
,
76 OUT PULONG CpuInfoEcx
,
77 OUT PULONG CpuInfoEdx
)
81 /* Perform the CPUID Operation */
82 __cpuid((int*)CpuInfo
, InfoType
);
84 /* Return the results */
85 *CpuInfoEax
= CpuInfo
[0];
86 *CpuInfoEbx
= CpuInfo
[1];
87 *CpuInfoEcx
= CpuInfo
[2];
88 *CpuInfoEdx
= CpuInfo
[3];
93 WRMSR(IN ULONG Register
,
96 /* Write to the MSR */
97 __writemsr(Register
, Value
);
102 RDMSR(IN ULONG Register
)
104 /* Read from the MSR */
105 return __readmsr(Register
);
108 /* NSC/Cyrix CPU configuration register index */
109 #define CX86_CCR1 0xc1
111 /* NSC/Cyrix CPU indexed register access macros */
116 WRITE_PORT_UCHAR((PUCHAR
)(ULONG_PTR
)0x22, reg
);
117 return READ_PORT_UCHAR((PUCHAR
)(ULONG_PTR
)0x23);
120 #define setCx86(reg, data) do { \
121 WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x22,(reg)); \
122 WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x23,(data)); \
125 /* FUNCTIONS *****************************************************************/
130 KiSetProcessorType(VOID
)
132 ULONG EFlags
, NewEFlags
;
134 ULONG Stepping
, Type
;
136 /* Start by assuming no CPUID data */
137 KeGetCurrentPrcb()->CpuID
= 0;
140 EFlags
= __readeflags();
142 /* XOR out the ID bit and update EFlags */
143 NewEFlags
= EFlags
^ EFLAGS_ID
;
144 __writeeflags(NewEFlags
);
146 /* Get them back and see if they were modified */
147 NewEFlags
= __readeflags();
148 if (NewEFlags
!= EFlags
)
150 /* The modification worked, so CPUID exists. Set the ID Bit again. */
152 __writeeflags(EFlags
);
154 /* Peform CPUID 0 to see if CPUID 1 is supported */
155 CPUID(0, &Reg
, &Dummy
, &Dummy
, &Dummy
);
159 CPUID(1, &Reg
, &Dummy
, &Dummy
, &Dummy
);
162 * Get the Stepping and Type. The stepping contains both the
163 * Model and the Step, while the Type contains the returned Type.
164 * We ignore the family.
166 * For the stepping, we convert this: zzzzzzxy into this: x0y
168 Stepping
= Reg
& 0xF0;
170 Stepping
+= (Reg
& 0xFF);
175 /* Save them in the PRCB */
176 KeGetCurrentPrcb()->CpuID
= TRUE
;
177 KeGetCurrentPrcb()->CpuType
= (UCHAR
)Type
;
178 KeGetCurrentPrcb()->CpuStep
= (USHORT
)Stepping
;
182 DPRINT1("CPUID Support lacking\n");
187 DPRINT1("CPUID Support lacking\n");
191 __writeeflags(EFlags
);
199 PKPRCB Prcb
= KeGetCurrentPrcb();
203 /* Assume no Vendor ID and fail if no CPUID Support. */
204 Prcb
->VendorString
[0] = 0;
205 if (!Prcb
->CpuID
) return 0;
207 /* Get the Vendor ID and null-terminate it */
208 CPUID(0, &Vendor
[0], &Vendor
[1], &Vendor
[2], &Vendor
[3]);
211 /* Re-arrange vendor string */
213 Vendor
[2] = Vendor
[3];
216 /* Copy it to the PRCB and null-terminate it again */
217 RtlCopyMemory(Prcb
->VendorString
,
219 sizeof(Prcb
->VendorString
) - sizeof(CHAR
));
220 Prcb
->VendorString
[sizeof(Prcb
->VendorString
) - sizeof(CHAR
)] = ANSI_NULL
;
222 /* Now check the CPU Type */
223 if (!strcmp(Prcb
->VendorString
, CmpIntelID
))
227 else if (!strcmp(Prcb
->VendorString
, CmpAmdID
))
231 else if (!strcmp(Prcb
->VendorString
, CmpCyrixID
))
233 DPRINT1("Cyrix CPU support not fully tested!\n");
236 else if (!strcmp(Prcb
->VendorString
, CmpTransmetaID
))
238 DPRINT1("Transmeta CPU support not fully tested!\n");
239 return CPU_TRANSMETA
;
241 else if (!strcmp(Prcb
->VendorString
, CmpCentaurID
))
243 DPRINT1("Centaur CPU support not fully tested!\n");
246 else if (!strcmp(Prcb
->VendorString
, CmpRiseID
))
248 DPRINT1("Rise CPU support not fully tested!\n");
259 KiGetFeatureBits(VOID
)
261 PKPRCB Prcb
= KeGetCurrentPrcb();
263 ULONG FeatureBits
= KF_WORKING_PTE
;
264 ULONG Reg
[4], Dummy
, Ccr1
;
265 BOOLEAN ExtendedCPUID
= TRUE
;
266 ULONG CpuFeatures
= 0;
268 /* Get the Vendor ID */
269 Vendor
= KiGetCpuVendor();
271 /* Make sure we got a valid vendor ID at least. */
272 if (!Vendor
) return FeatureBits
;
274 /* Get the CPUID Info. Features are in Reg[3]. */
275 CPUID(1, &Reg
[0], &Reg
[1], &Dummy
, &Reg
[3]);
277 /* Set the initial APIC ID */
278 Prcb
->InitialApicId
= (UCHAR
)(Reg
[1] >> 24);
285 /* Check if it's a P6 */
286 if (Prcb
->CpuType
== 6)
288 /* Perform the special sequence to get the MicroCode Signature */
290 CPUID(1, &Dummy
, &Dummy
, &Dummy
, &Dummy
);
291 Prcb
->UpdateSignature
.QuadPart
= RDMSR(0x8B);
293 else if (Prcb
->CpuType
== 5)
295 /* On P5, enable workaround for the LOCK errata. */
296 KiI386PentiumLockErrataPresent
= TRUE
;
299 /* Check for broken P6 with bad SMP PTE implementation */
300 if (((Reg
[0] & 0x0FF0) == 0x0610 && (Reg
[0] & 0x000F) <= 0x9) ||
301 ((Reg
[0] & 0x0FF0) == 0x0630 && (Reg
[0] & 0x000F) <= 0x4))
303 /* Remove support for correct PTE support. */
304 FeatureBits
&= ~KF_WORKING_PTE
;
307 /* Virtualbox claims to have no SYSENTER support,
308 * which is false for processors >= Pentium Pro */
309 if(Prcb
->CpuType
>= 6)
314 /* Check if the CPU is too old to support SYSENTER,
315 * See Intel CPUID instruction manual for details*/
316 if ((Reg
[0] & 0x0FFF3FFF) < 0x00000633)
322 /* Set the current features */
323 CpuFeatures
= Reg
[3];
330 /* Check if this is a K5 or K6. (family 5) */
331 if ((Reg
[0] & 0x0F00) == 0x0500)
333 /* Get the Model Number */
334 switch (Reg
[0] & 0x00F0)
336 /* Model 1: K5 - 5k86 (initial models) */
339 /* Check if this is Step 0 or 1. They don't support PGE */
340 if ((Reg
[0] & 0x000F) > 0x03) break;
342 /* Model 0: K5 - SSA5 */
345 /* Model 0 doesn't support PGE at all. */
352 /* K6-2, Step 8 and over have support for MTRR. */
353 if ((Reg
[0] & 0x000F) >= 0x8) FeatureBits
|= KF_AMDK6MTRR
;
357 Model D: K6-2+, K6-III+ */
361 FeatureBits
|= KF_AMDK6MTRR
;
365 else if((Reg
[0] & 0x0F00) < 0x0500)
367 /* Families below 5 don't support PGE, PSE or CMOV at all */
368 Reg
[3] &= ~(0x08 | 0x2000 | 0x8000);
370 /* They also don't support advanced CPUID functions. */
371 ExtendedCPUID
= FALSE
;
374 /* Set the current features */
375 CpuFeatures
= Reg
[3];
382 /* Workaround the "COMA" bug on 6x family of Cyrix CPUs */
383 if (Prcb
->CpuType
== 6 &&
387 Ccr1
= getCx86(CX86_CCR1
);
389 /* Enable the NO_LOCK bit */
392 /* Set the new CCR1 value */
393 setCx86(CX86_CCR1
, Ccr1
);
396 /* Set the current features */
397 CpuFeatures
= Reg
[3];
404 /* Enable CMPXCHG8B if the family (>= 5), model and stepping (>= 4.2) support it */
405 if ((Reg
[0] & 0x0FFF) >= 0x0542)
407 WRMSR(0x80860004, RDMSR(0x80860004) | 0x0100);
408 FeatureBits
|= KF_CMPXCHG8B
;
413 /* Centaur, IDT, Rise and VIA CPUs */
417 /* These CPUs don't report the presence of CMPXCHG8B through CPUID.
418 However, this feature exists and operates properly without any additional steps. */
419 FeatureBits
|= KF_CMPXCHG8B
;
424 /* Convert all CPUID Feature bits into our format */
425 if (CpuFeatures
& 0x00000002) FeatureBits
|= KF_V86_VIS
| KF_CR4
;
426 if (CpuFeatures
& 0x00000008) FeatureBits
|= KF_LARGE_PAGE
| KF_CR4
;
427 if (CpuFeatures
& 0x00000010) FeatureBits
|= KF_RDTSC
;
428 if (CpuFeatures
& 0x00000100) FeatureBits
|= KF_CMPXCHG8B
;
429 if (CpuFeatures
& 0x00000800) FeatureBits
|= KF_FAST_SYSCALL
;
430 if (CpuFeatures
& 0x00001000) FeatureBits
|= KF_MTRR
;
431 if (CpuFeatures
& 0x00002000) FeatureBits
|= KF_GLOBAL_PAGE
| KF_CR4
;
432 if (CpuFeatures
& 0x00008000) FeatureBits
|= KF_CMOV
;
433 if (CpuFeatures
& 0x00010000) FeatureBits
|= KF_PAT
;
434 if (CpuFeatures
& 0x00200000) FeatureBits
|= KF_DTS
;
435 if (CpuFeatures
& 0x00800000) FeatureBits
|= KF_MMX
;
436 if (CpuFeatures
& 0x01000000) FeatureBits
|= KF_FXSR
;
437 if (CpuFeatures
& 0x02000000) FeatureBits
|= KF_XMMI
;
438 if (CpuFeatures
& 0x04000000) FeatureBits
|= KF_XMMI64
;
440 /* Check if the CPU has hyper-threading */
441 if (CpuFeatures
& 0x10000000)
443 /* Set the number of logical CPUs */
444 Prcb
->LogicalProcessorsPerPhysicalProcessor
= (UCHAR
)(Reg
[1] >> 16);
445 if (Prcb
->LogicalProcessorsPerPhysicalProcessor
> 1)
447 /* We're on dual-core */
448 KiSMTProcessorsPresent
= TRUE
;
453 /* We only have a single CPU */
454 Prcb
->LogicalProcessorsPerPhysicalProcessor
= 1;
457 /* Check if CPUID 0x80000000 is supported */
461 CPUID(0x80000000, &Reg
[0], &Dummy
, &Dummy
, &Dummy
);
462 if ((Reg
[0] & 0xffffff00) == 0x80000000)
464 /* Check if CPUID 0x80000001 is supported */
465 if (Reg
[0] >= 0x80000001)
467 /* Check which extended features are available. */
468 CPUID(0x80000001, &Dummy
, &Dummy
, &Dummy
, &Reg
[3]);
470 /* Check if NX-bit is supported */
471 if (Reg
[3] & 0x00100000) FeatureBits
|= KF_NX_BIT
;
473 /* Now handle each features for each CPU Vendor */
478 if (Reg
[3] & 0x80000000) FeatureBits
|= KF_3DNOW
;
485 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",
486 #define print_supported(kf_value) \
487 FeatureBits & kf_value ? #kf_value : ""
488 print_supported(KF_V86_VIS
),
489 print_supported(KF_RDTSC
),
490 print_supported(KF_CR4
),
491 print_supported(KF_CMOV
),
492 print_supported(KF_GLOBAL_PAGE
),
493 print_supported(KF_LARGE_PAGE
),
494 print_supported(KF_MTRR
),
495 print_supported(KF_CMPXCHG8B
),
496 print_supported(KF_MMX
),
497 print_supported(KF_WORKING_PTE
),
498 print_supported(KF_PAT
),
499 print_supported(KF_FXSR
),
500 print_supported(KF_FAST_SYSCALL
),
501 print_supported(KF_XMMI
),
502 print_supported(KF_3DNOW
),
503 print_supported(KF_AMDK6MTRR
),
504 print_supported(KF_XMMI64
),
505 print_supported(KF_DTS
),
506 print_supported(KF_NX_BIT
),
507 print_supported(KF_NX_DISABLED
),
508 print_supported(KF_NX_ENABLED
));
509 #undef print_supported
511 /* Return the Feature Bits */
518 KiGetCacheInformation(VOID
)
520 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
522 ULONG Data
[4], Dummy
;
523 ULONG CacheRequests
= 0, i
;
524 ULONG CurrentRegister
;
526 ULONG Size
, Associativity
= 0, CacheLine
= 64, CurrentSize
= 0;
527 BOOLEAN FirstPass
= TRUE
;
529 /* Set default L2 size */
530 Pcr
->SecondLevelCacheSize
= 0;
532 /* Get the Vendor ID and make sure we support CPUID */
533 Vendor
= KiGetCpuVendor();
536 /* Check the Vendor ID */
539 /* Handle Intel case */
542 /*Check if we support CPUID 2 */
543 CPUID(0, &Data
[0], &Dummy
, &Dummy
, &Dummy
);
546 /* We need to loop for the number of times CPUID will tell us to */
549 /* Do the CPUID call */
550 CPUID(2, &Data
[0], &Data
[1], &Data
[2], &Data
[3]);
552 /* Check if it was the first call */
556 * The number of times to loop is the first byte. Read
557 * it and then destroy it so we don't get confused.
559 CacheRequests
= Data
[0] & 0xFF;
560 Data
[0] &= 0xFFFFFF00;
562 /* Don't go over this again */
566 /* Loop all 4 registers */
567 for (i
= 0; i
< 4; i
++)
569 /* Get the current register */
570 CurrentRegister
= Data
[i
];
573 * If the upper bit is set, then this register should
576 if (CurrentRegister
& 0x80000000) continue;
578 /* Keep looping for every byte inside this register */
579 while (CurrentRegister
)
581 /* Read a byte, skip a byte. */
582 RegisterByte
= (UCHAR
)(CurrentRegister
& 0xFF);
583 CurrentRegister
>>= 8;
584 if (!RegisterByte
) continue;
587 * Valid values are from 0x40 (0 bytes) to 0x49
588 * (32MB), or from 0x80 to 0x89 (same size but
591 if (((RegisterByte
> 0x40) && (RegisterByte
<= 0x47)) ||
592 ((RegisterByte
> 0x78) && (RegisterByte
<= 0x7C)) ||
593 ((RegisterByte
> 0x80) && (RegisterByte
<= 0x85)))
595 /* Compute associativity */
597 if (RegisterByte
>= 0x79) Associativity
= 8;
599 /* Mask out only the first nibble */
600 RegisterByte
&= 0x07;
602 /* Check if this cache is bigger than the last */
603 Size
= 0x10000 << RegisterByte
;
604 if ((Size
/ Associativity
) > CurrentSize
)
606 /* Set the L2 Cache Size and Associativity */
607 CurrentSize
= Size
/ Associativity
;
608 Pcr
->SecondLevelCacheSize
= Size
;
609 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
612 else if ((RegisterByte
> 0x21) && (RegisterByte
<= 0x29))
614 /* Set minimum cache line size */
615 if (CacheLine
< 128) CacheLine
= 128;
617 /* Hard-code size/associativity */
619 switch (RegisterByte
)
643 /* Check if this cache is bigger than the last */
644 if ((Size
/ Associativity
) > CurrentSize
)
646 /* Set the L2 Cache Size and Associativity */
647 CurrentSize
= Size
/ Associativity
;
648 Pcr
->SecondLevelCacheSize
= Size
;
649 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
652 else if (((RegisterByte
> 0x65) && (RegisterByte
< 0x69)) ||
653 (RegisterByte
== 0x2C) || (RegisterByte
== 0xF0))
655 /* Indicates L1 cache line of 64 bytes */
656 KePrefetchNTAGranularity
= 64;
658 else if (RegisterByte
== 0xF1)
660 /* Indicates L1 cache line of 128 bytes */
661 KePrefetchNTAGranularity
= 128;
663 else if (((RegisterByte
>= 0x4A) && (RegisterByte
<= 0x4C)) ||
664 (RegisterByte
== 0x78) ||
665 (RegisterByte
== 0x7D) ||
666 (RegisterByte
== 0x7F) ||
667 (RegisterByte
== 0x86) ||
668 (RegisterByte
== 0x87))
670 /* Set minimum cache line size */
671 if (CacheLine
< 64) CacheLine
= 64;
673 /* Hard-code size/associativity */
674 switch (RegisterByte
)
677 Size
= 4 * 1024 * 1024;
682 Size
= 6 * 1024 * 1024;
687 Size
= 8 * 1024 * 1024;
692 Size
= 1 * 1024 * 1024;
697 Size
= 2 * 1024 * 1024;
712 Size
= 1 * 1024 * 1024;
721 /* Check if this cache is bigger than the last */
722 if ((Size
/ Associativity
) > CurrentSize
)
724 /* Set the L2 Cache Size and Associativity */
725 CurrentSize
= Size
/ Associativity
;
726 Pcr
->SecondLevelCacheSize
= Size
;
727 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
732 } while (--CacheRequests
);
738 /* Check if we support CPUID 0x80000005 */
739 CPUID(0x80000000, &Data
[0], &Data
[1], &Data
[2], &Data
[3]);
740 if (Data
[0] >= 0x80000006)
742 /* Get L1 size first */
743 CPUID(0x80000005, &Data
[0], &Data
[1], &Data
[2], &Data
[3]);
744 KePrefetchNTAGranularity
= Data
[2] & 0xFF;
746 /* Check if we support CPUID 0x80000006 */
747 CPUID(0x80000000, &Data
[0], &Data
[1], &Data
[2], &Data
[3]);
748 if (Data
[0] >= 0x80000006)
750 /* Get 2nd level cache and tlb size */
751 CPUID(0x80000006, &Data
[0], &Data
[1], &Data
[2], &Data
[3]);
753 /* Cache line size */
754 CacheLine
= Data
[2] & 0xFF;
756 /* Hardcode associativity */
757 RegisterByte
= Data
[2] >> 12;
758 switch (RegisterByte
)
783 Size
= (Data
[2] >> 16) << 10;
785 /* Hack for Model 6, Steping 300 */
786 if ((KeGetCurrentPrcb()->CpuType
== 6) &&
787 (KeGetCurrentPrcb()->CpuStep
== 0x300))
789 /* Stick 64K in there */
793 /* Set the L2 Cache Size and associativity */
794 Pcr
->SecondLevelCacheSize
= Size
;
795 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
809 /* Set the cache line */
810 if (CacheLine
> KeLargestCacheLine
) KeLargestCacheLine
= CacheLine
;
811 DPRINT1("Prefetch Cache: %d bytes\tL2 Cache: %d bytes\tL2 Cache Line: %d bytes\tL2 Cache Associativity: %d\n",
812 KePrefetchNTAGranularity
,
813 Pcr
->SecondLevelCacheSize
,
815 Pcr
->SecondLevelCacheAssociativity
);
825 /* Save current CR0 */
828 /* If this is a 486, enable Write-Protection */
829 if (KeGetCurrentPrcb()->CpuType
> 3) Cr0
|= CR0_WP
;
838 KiInitializeTSS2(IN PKTSS Tss
,
839 IN PKGDTENTRY TssEntry OPTIONAL
)
843 /* Make sure the GDT Entry is valid */
847 TssEntry
->LimitLow
= sizeof(KTSS
) - 1;
848 TssEntry
->HighWord
.Bits
.LimitHi
= 0;
851 /* Now clear the I/O Map */
852 ASSERT(IOPM_COUNT
== 1);
853 RtlFillMemory(Tss
->IoMaps
[0].IoMap
, IOPM_FULL_SIZE
, 0xFF);
855 /* Initialize Interrupt Direction Maps */
856 p
= (PUCHAR
)(Tss
->IoMaps
[0].DirectionMap
);
857 RtlZeroMemory(p
, IOPM_DIRECTION_MAP_SIZE
);
859 /* Add DPMI support for interrupts */
864 /* Initialize the default Interrupt Direction Map */
865 p
= Tss
->IntDirectionMap
;
866 RtlZeroMemory(Tss
->IntDirectionMap
, IOPM_DIRECTION_MAP_SIZE
);
868 /* Add DPMI support */
876 KiInitializeTSS(IN PKTSS Tss
)
878 /* Set an invalid map base */
879 Tss
->IoMapBase
= KiComputeIopmOffset(IO_ACCESS_MAP_NONE
);
881 /* Disable traps during Task Switches */
884 /* Set LDT and Ring 0 SS */
886 Tss
->Ss0
= KGDT_R0_DATA
;
892 Ki386InitializeTss(IN PKTSS Tss
,
896 PKGDTENTRY TssEntry
, TaskGateEntry
;
898 /* Initialize the boot TSS. */
899 TssEntry
= &Gdt
[KGDT_TSS
/ sizeof(KGDTENTRY
)];
900 TssEntry
->HighWord
.Bits
.Type
= I386_TSS
;
901 TssEntry
->HighWord
.Bits
.Pres
= 1;
902 TssEntry
->HighWord
.Bits
.Dpl
= 0;
903 KiInitializeTSS2(Tss
, TssEntry
);
904 KiInitializeTSS(Tss
);
906 /* Load the task register */
907 Ke386SetTr(KGDT_TSS
);
909 /* Setup the Task Gate for Double Fault Traps */
910 TaskGateEntry
= (PKGDTENTRY
)&Idt
[8];
911 TaskGateEntry
->HighWord
.Bits
.Type
= I386_TASK_GATE
;
912 TaskGateEntry
->HighWord
.Bits
.Pres
= 1;
913 TaskGateEntry
->HighWord
.Bits
.Dpl
= 0;
914 ((PKIDTENTRY
)TaskGateEntry
)->Selector
= KGDT_DF_TSS
;
916 /* Initialize the TSS used for handling double faults. */
917 Tss
= (PKTSS
)KiDoubleFaultTSS
;
918 KiInitializeTSS(Tss
);
919 Tss
->CR3
= __readcr3();
920 Tss
->Esp0
= KiDoubleFaultStack
;
921 Tss
->Esp
= KiDoubleFaultStack
;
922 Tss
->Eip
= PtrToUlong(KiTrap08
);
923 Tss
->Cs
= KGDT_R0_CODE
;
924 Tss
->Fs
= KGDT_R0_PCR
;
925 Tss
->Ss
= Ke386GetSs();
926 Tss
->Es
= KGDT_R3_DATA
| RPL_MASK
;
927 Tss
->Ds
= KGDT_R3_DATA
| RPL_MASK
;
929 /* Setup the Double Trap TSS entry in the GDT */
930 TssEntry
= &Gdt
[KGDT_DF_TSS
/ sizeof(KGDTENTRY
)];
931 TssEntry
->HighWord
.Bits
.Type
= I386_TSS
;
932 TssEntry
->HighWord
.Bits
.Pres
= 1;
933 TssEntry
->HighWord
.Bits
.Dpl
= 0;
934 TssEntry
->BaseLow
= (USHORT
)((ULONG_PTR
)Tss
& 0xFFFF);
935 TssEntry
->HighWord
.Bytes
.BaseMid
= (UCHAR
)((ULONG_PTR
)Tss
>> 16);
936 TssEntry
->HighWord
.Bytes
.BaseHi
= (UCHAR
)((ULONG_PTR
)Tss
>> 24);
937 TssEntry
->LimitLow
= KTSS_IO_MAPS
;
939 /* Now setup the NMI Task Gate */
940 TaskGateEntry
= (PKGDTENTRY
)&Idt
[2];
941 TaskGateEntry
->HighWord
.Bits
.Type
= I386_TASK_GATE
;
942 TaskGateEntry
->HighWord
.Bits
.Pres
= 1;
943 TaskGateEntry
->HighWord
.Bits
.Dpl
= 0;
944 ((PKIDTENTRY
)TaskGateEntry
)->Selector
= KGDT_NMI_TSS
;
946 /* Initialize the actual TSS */
947 Tss
= (PKTSS
)KiNMITSS
;
948 KiInitializeTSS(Tss
);
949 Tss
->CR3
= __readcr3();
950 Tss
->Esp0
= KiDoubleFaultStack
;
951 Tss
->Esp
= KiDoubleFaultStack
;
952 Tss
->Eip
= PtrToUlong(KiTrap02
);
953 Tss
->Cs
= KGDT_R0_CODE
;
954 Tss
->Fs
= KGDT_R0_PCR
;
955 Tss
->Ss
= Ke386GetSs();
956 Tss
->Es
= KGDT_R3_DATA
| RPL_MASK
;
957 Tss
->Ds
= KGDT_R3_DATA
| RPL_MASK
;
959 /* And its associated TSS Entry */
960 TssEntry
= &Gdt
[KGDT_NMI_TSS
/ sizeof(KGDTENTRY
)];
961 TssEntry
->HighWord
.Bits
.Type
= I386_TSS
;
962 TssEntry
->HighWord
.Bits
.Pres
= 1;
963 TssEntry
->HighWord
.Bits
.Dpl
= 0;
964 TssEntry
->BaseLow
= (USHORT
)((ULONG_PTR
)Tss
& 0xFFFF);
965 TssEntry
->HighWord
.Bytes
.BaseMid
= (UCHAR
)((ULONG_PTR
)Tss
>> 16);
966 TssEntry
->HighWord
.Bytes
.BaseHi
= (UCHAR
)((ULONG_PTR
)Tss
>> 24);
967 TssEntry
->LimitLow
= KTSS_IO_MAPS
;
972 KeFlushCurrentTb(VOID
)
974 /* Flush the TLB by resetting CR3 */
975 __writecr3(__readcr3());
980 KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState
)
985 // Restore the CR registers
987 __writecr0(ProcessorState
->SpecialRegisters
.Cr0
);
988 Ke386SetCr2(ProcessorState
->SpecialRegisters
.Cr2
);
989 __writecr3(ProcessorState
->SpecialRegisters
.Cr3
);
990 if (KeFeatureBits
& KF_CR4
) __writecr4(ProcessorState
->SpecialRegisters
.Cr4
);
993 // Restore the DR registers
995 __writedr(0, ProcessorState
->SpecialRegisters
.KernelDr0
);
996 __writedr(1, ProcessorState
->SpecialRegisters
.KernelDr1
);
997 __writedr(2, ProcessorState
->SpecialRegisters
.KernelDr2
);
998 __writedr(3, ProcessorState
->SpecialRegisters
.KernelDr3
);
999 __writedr(6, ProcessorState
->SpecialRegisters
.KernelDr6
);
1000 __writedr(7, ProcessorState
->SpecialRegisters
.KernelDr7
);
1003 // Restore GDT and IDT
1005 Ke386SetGlobalDescriptorTable(&ProcessorState
->SpecialRegisters
.Gdtr
.Limit
);
1006 __lidt(&ProcessorState
->SpecialRegisters
.Idtr
.Limit
);
1009 // Clear the busy flag so we don't crash if we reload the same selector
1011 TssEntry
= (PKGDTENTRY
)(ProcessorState
->SpecialRegisters
.Gdtr
.Base
+
1012 ProcessorState
->SpecialRegisters
.Tr
);
1013 TssEntry
->HighWord
.Bytes
.Flags1
&= ~0x2;
1016 // Restore TSS and LDT
1018 Ke386SetTr(ProcessorState
->SpecialRegisters
.Tr
);
1019 Ke386SetLocalDescriptorTable(ProcessorState
->SpecialRegisters
.Ldtr
);
1024 KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState
)
1026 /* Save the CR registers */
1027 ProcessorState
->SpecialRegisters
.Cr0
= __readcr0();
1028 ProcessorState
->SpecialRegisters
.Cr2
= __readcr2();
1029 ProcessorState
->SpecialRegisters
.Cr3
= __readcr3();
1030 ProcessorState
->SpecialRegisters
.Cr4
= (KeFeatureBits
& KF_CR4
) ?
1033 /* Save the DR registers */
1034 ProcessorState
->SpecialRegisters
.KernelDr0
= __readdr(0);
1035 ProcessorState
->SpecialRegisters
.KernelDr1
= __readdr(1);
1036 ProcessorState
->SpecialRegisters
.KernelDr2
= __readdr(2);
1037 ProcessorState
->SpecialRegisters
.KernelDr3
= __readdr(3);
1038 ProcessorState
->SpecialRegisters
.KernelDr6
= __readdr(6);
1039 ProcessorState
->SpecialRegisters
.KernelDr7
= __readdr(7);
1042 /* Save GDT, IDT, LDT and TSS */
1043 Ke386GetGlobalDescriptorTable(&ProcessorState
->SpecialRegisters
.Gdtr
.Limit
);
1044 __sidt(&ProcessorState
->SpecialRegisters
.Idtr
.Limit
);
1045 ProcessorState
->SpecialRegisters
.Tr
= Ke386GetTr();
1046 ProcessorState
->SpecialRegisters
.Ldtr
= Ke386GetLocalDescriptorTable();
1052 KiInitializeMachineType(VOID
)
1054 /* Set the Machine Type we got from NTLDR */
1055 KeI386MachineType
= KeLoaderBlock
->u
.I386
.MachineType
& 0x000FF;
1061 KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context
)
1063 /* Set CS and ESP */
1064 WRMSR(0x174, KGDT_R0_CODE
);
1065 WRMSR(0x175, (ULONG_PTR
)KeGetCurrentPrcb()->DpcStack
);
1068 WRMSR(0x176, (ULONG_PTR
)KiFastCallEntry
);
1075 KiRestoreFastSyscallReturnState(VOID
)
1077 /* Check if the CPU Supports fast system call */
1078 if (KeFeatureBits
& KF_FAST_SYSCALL
)
1080 /* Check if it has been disabled */
1081 if (!KiFastSystemCallDisable
)
1083 /* Do an IPI to enable it */
1084 KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters
, 0);
1086 /* It's enabled, so use the proper exit stub */
1087 KiFastCallExitHandler
= KiSystemCallSysExitReturn
;
1088 DPRINT1("Support for SYSENTER detected.\n");
1092 /* Disable fast system call */
1093 KeFeatureBits
&= ~KF_FAST_SYSCALL
;
1094 KiFastCallExitHandler
= KiSystemCallTrapReturn
;
1095 DPRINT1("Support for SYSENTER disabled.\n");
1100 /* Use the IRET handler */
1101 KiFastCallExitHandler
= KiSystemCallTrapReturn
;
1102 DPRINT1("No support for SYSENTER detected.\n");
1109 Ki386EnableDE(IN ULONG_PTR Context
)
1112 __writecr4(__readcr4() | CR4_DE
);
1119 Ki386EnableFxsr(IN ULONG_PTR Context
)
1122 __writecr4(__readcr4() | CR4_FXSR
);
1129 Ki386EnableXMMIExceptions(IN ULONG_PTR Context
)
1131 PKIDTENTRY IdtEntry
;
1133 /* Get the IDT Entry for Interrupt 0x13 */
1134 IdtEntry
= &((PKIPCR
)KeGetPcr())->IDT
[0x13];
1137 IdtEntry
->Selector
= KGDT_R0_CODE
;
1138 IdtEntry
->Offset
= ((ULONG_PTR
)KiTrap13
& 0xFFFF);
1139 IdtEntry
->ExtendedOffset
= ((ULONG_PTR
)KiTrap13
>> 16) & 0xFFFF;
1140 ((PKIDT_ACCESS
)&IdtEntry
->Access
)->Dpl
= 0;
1141 ((PKIDT_ACCESS
)&IdtEntry
->Access
)->Present
= 1;
1142 ((PKIDT_ACCESS
)&IdtEntry
->Access
)->SegmentType
= I386_INTERRUPT_GATE
;
1144 /* Enable XMMI exceptions */
1145 __writecr4(__readcr4() | CR4_XMMEXCPT
);
1152 KiI386PentiumLockErrataFixup(VOID
)
1154 KDESCRIPTOR IdtDescriptor
;
1155 PKIDTENTRY NewIdt
, NewIdt2
;
1157 /* Allocate memory for a new IDT */
1158 NewIdt
= ExAllocatePool(NonPagedPool
, 2 * PAGE_SIZE
);
1160 /* Put everything after the first 7 entries on a new page */
1161 NewIdt2
= (PVOID
)((ULONG_PTR
)NewIdt
+ PAGE_SIZE
- (7 * sizeof(KIDTENTRY
)));
1163 /* Disable interrupts */
1166 /* Get the current IDT and copy it */
1167 __sidt(&IdtDescriptor
.Limit
);
1168 RtlCopyMemory(NewIdt2
,
1169 (PVOID
)IdtDescriptor
.Base
,
1170 IdtDescriptor
.Limit
+ 1);
1171 IdtDescriptor
.Base
= (ULONG
)NewIdt2
;
1173 /* Set the new IDT */
1174 __lidt(&IdtDescriptor
.Limit
);
1175 ((PKIPCR
)KeGetPcr())->IDT
= NewIdt2
;
1177 /* Restore interrupts */
1180 /* Set the first 7 entries as read-only to produce a fault */
1181 MmSetPageProtect(NULL
, NewIdt
, PAGE_READONLY
);
1186 KeDisableInterrupts(VOID
)
1191 /* Get EFLAGS and check if the interrupt bit is set */
1192 Flags
= __readeflags();
1193 Return
= (Flags
& EFLAGS_INTERRUPT_MASK
) ? TRUE
: FALSE
;
1195 /* Disable interrupts */
1202 KeInvalidateAllCaches(VOID
)
1204 /* Only supported on Pentium Pro and higher */
1205 if (KeI386CpuType
< 6) return FALSE
;
1207 /* Invalidate all caches */
1214 KeZeroPages(IN PVOID Address
,
1217 /* Not using XMMI in this routine */
1218 RtlZeroMemory(Address
, Size
);
1223 KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame
,
1224 IN PKEXCEPTION_FRAME ExceptionFrame
)
1226 PKPRCB Prcb
= KeGetCurrentPrcb();
1229 // Save full context
1231 Prcb
->ProcessorState
.ContextFrame
.ContextFlags
= CONTEXT_FULL
|
1232 CONTEXT_DEBUG_REGISTERS
;
1233 KeTrapFrameToContext(TrapFrame
, NULL
, &Prcb
->ProcessorState
.ContextFrame
);
1236 // Save control registers
1238 KiSaveProcessorControlState(&Prcb
->ProcessorState
);
1244 KiIsNpxPresent(VOID
)
1252 /* Read CR0 and mask out FPU flags */
1253 Cr0
= __readcr0() & ~(CR0_MP
| CR0_TS
| CR0_EM
| CR0_ET
);
1255 /* Store on FPU stack */
1260 asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic
));
1263 /* Magic should now be cleared */
1266 /* You don't have an FPU -- enable emulation for now */
1267 __writecr0(Cr0
| CR0_EM
| CR0_TS
);
1271 /* You have an FPU, enable it */
1274 /* Enable INT 16 on 486 and higher */
1275 if (KeGetCurrentPrcb()->CpuType
>= 3) Cr0
|= CR0_NE
;
1278 __writecr0(Cr0
| CR0_EM
| CR0_TS
);
1285 KiIsNpxErrataPresent(VOID
)
1287 BOOLEAN ErrataPresent
;
1289 volatile double Value1
, Value2
;
1291 /* Disable interrupts */
1294 /* Read CR0 and remove FPU flags */
1296 __writecr0(Cr0
& ~(CR0_MP
| CR0_TS
| CR0_EM
));
1298 /* Initialize FPU state */
1301 /* Multiply the magic values and divide, we should get the result back */
1304 ErrataPresent
= (Value1
* Value2
/ 3145727.0) != 4195835.0;
1309 /* Enable interrupts */
1312 /* Return if there's an errata */
1313 return ErrataPresent
;
1318 KiFlushNPXState(IN PFLOATING_SAVE_AREA SaveArea
)
1321 PKTHREAD Thread
, NpxThread
;
1322 PFX_SAVE_AREA FxSaveArea
;
1324 /* Save volatiles and disable interrupts */
1325 EFlags
= __readeflags();
1328 /* Save the PCR and get the current thread */
1329 Thread
= KeGetCurrentThread();
1331 /* Check if we're already loaded */
1332 if (Thread
->NpxState
!= NPX_STATE_LOADED
)
1334 /* If there's nothing to load, quit */
1335 if (!SaveArea
) return;
1337 /* Need FXSR support for this */
1338 ASSERT(KeI386FxsrPresent
== TRUE
);
1340 /* Check for sane CR0 */
1342 if (Cr0
& (CR0_MP
| CR0_TS
| CR0_EM
))
1344 /* Mask out FPU flags */
1345 __writecr0(Cr0
& ~(CR0_MP
| CR0_TS
| CR0_EM
));
1348 /* Get the NPX thread and check its FPU state */
1349 NpxThread
= KeGetCurrentPrcb()->NpxThread
;
1350 if ((NpxThread
) && (NpxThread
->NpxState
== NPX_STATE_LOADED
))
1352 /* Get the FX frame and store the state there */
1353 FxSaveArea
= KiGetThreadNpxArea(NpxThread
);
1354 Ke386FxSave(FxSaveArea
);
1356 /* NPX thread has lost its state */
1357 NpxThread
->NpxState
= NPX_STATE_NOT_LOADED
;
1360 /* Now load NPX state from the NPX area */
1361 FxSaveArea
= KiGetThreadNpxArea(Thread
);
1362 Ke386FxStore(FxSaveArea
);
1366 /* Check for sane CR0 */
1368 if (Cr0
& (CR0_MP
| CR0_TS
| CR0_EM
))
1370 /* Mask out FPU flags */
1371 __writecr0(Cr0
& ~(CR0_MP
| CR0_TS
| CR0_EM
));
1375 FxSaveArea
= KiGetThreadNpxArea(Thread
);
1376 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
1378 /* Save state if supported by CPU */
1379 if (KeI386FxsrPresent
) Ke386FxSave(FxSaveArea
);
1382 /* Now save the FN state wherever it was requested */
1383 if (SaveArea
) Ke386FnSave(SaveArea
);
1385 /* Clear NPX thread */
1386 KeGetCurrentPrcb()->NpxThread
= NULL
;
1388 /* Add the CR0 from the NPX frame */
1389 Cr0
|= NPX_STATE_NOT_LOADED
;
1390 Cr0
|= FxSaveArea
->Cr0NpxState
;
1393 /* Restore interrupt state */
1394 __writeeflags(EFlags
);
1397 /* PUBLIC FUNCTIONS **********************************************************/
1404 KiCoprocessorError(VOID
)
1406 PFX_SAVE_AREA NpxArea
;
1408 /* Get the FPU area */
1409 NpxArea
= KiGetThreadNpxArea(KeGetCurrentThread());
1412 NpxArea
->Cr0NpxState
= CR0_TS
;
1413 __writecr0(__readcr0() | CR0_TS
);
1421 KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save
)
1423 PFNSAVE_FORMAT FpState
;
1424 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
1425 DPRINT1("%s is not really implemented\n", __FUNCTION__
);
1427 /* check if we are doing software emulation */
1428 if (!KeI386NpxPresent
) return STATUS_ILLEGAL_FLOAT_CONTEXT
;
1430 FpState
= ExAllocatePool(NonPagedPool
, sizeof (FNSAVE_FORMAT
));
1431 if (!FpState
) return STATUS_INSUFFICIENT_RESOURCES
;
1433 *((PVOID
*) Save
) = FpState
;
1435 asm volatile("fnsave %0\n\t" : "=m" (*FpState
));
1443 KeGetCurrentThread()->DispatcherHeader
.NpxIrql
= KeGetCurrentIrql();
1444 return STATUS_SUCCESS
;
1452 KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save
)
1454 PFNSAVE_FORMAT FpState
= *((PVOID
*) Save
);
1455 ASSERT(KeGetCurrentThread()->DispatcherHeader
.NpxIrql
== KeGetCurrentIrql());
1456 DPRINT1("%s is not really implemented\n", __FUNCTION__
);
1459 asm volatile("fnclex\n\t");
1460 asm volatile("frstor %0\n\t" : "=m" (*FpState
));
1469 ExFreePool(FpState
);
1470 return STATUS_SUCCESS
;
1478 KeGetRecommendedSharedDataAlignment(VOID
)
1480 /* Return the global variable */
1481 return KeLargestCacheLine
;
1486 KiFlushTargetEntireTb(IN PKIPI_CONTEXT PacketContext
,
1491 /* Signal this packet as done */
1492 KiIpiSignalPacketDone(PacketContext
);
1494 /* Flush the TB for the Current CPU */
1503 KeFlushEntireTb(IN BOOLEAN Invalid
,
1504 IN BOOLEAN AllProcessors
)
1508 KAFFINITY TargetAffinity
;
1509 PKPRCB Prcb
= KeGetCurrentPrcb();
1512 /* Raise the IRQL for the TB Flush */
1513 OldIrql
= KeRaiseIrqlToSynchLevel();
1516 /* FIXME: Use KiTbFlushTimeStamp to synchronize TB flush */
1518 /* Get the current processor affinity, and exclude ourselves */
1519 TargetAffinity
= KeActiveProcessors
;
1520 TargetAffinity
&= ~Prcb
->SetMember
;
1522 /* Make sure this is MP */
1525 /* Send an IPI TB flush to the other processors */
1526 KiIpiSendPacket(TargetAffinity
,
1527 KiFlushTargetEntireTb
,
1534 /* Flush the TB for the Current CPU, and update the flush stamp */
1538 /* If this is MP, wait for the other processors to finish */
1542 ASSERT(Prcb
== (volatile PKPRCB
)KeGetCurrentPrcb());
1545 ASSERTMSG("Not yet implemented\n", FALSE
);
1549 /* Update the flush stamp and return to original IRQL */
1550 InterlockedExchangeAdd(&KiTbFlushTimeStamp
, 1);
1551 KeLowerIrql(OldIrql
);
1559 KeSetDmaIoCoherency(IN ULONG Coherency
)
1561 /* Save the coherency globally */
1562 KiDmaIoCoherency
= Coherency
;
1570 KeQueryActiveProcessors(VOID
)
1574 /* Simply return the number of active processors */
1575 return KeActiveProcessors
;
1583 KeSaveStateForHibernate(IN PKPROCESSOR_STATE State
)
1585 /* Capture the context */
1586 RtlCaptureContext(&State
->ContextFrame
);
1588 /* Capture the control state */
1589 KiSaveProcessorControlState(State
);