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 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
;
43 /* The distance between SYSEXIT and IRETD return modes */
44 UCHAR KiSystemCallExitAdjust
;
46 /* The offset that was applied -- either 0 or the value above */
47 UCHAR KiSystemCallExitAdjusted
;
49 /* Whether the adjustment was already done once */
50 BOOLEAN KiFastCallCopyDoneOnce
;
53 volatile LONG KiTbFlushTimeStamp
;
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";
63 /* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/
65 /* NSC/Cyrix CPU configuration register index */
66 #define CX86_CCR1 0xc1
68 /* NSC/Cyrix CPU indexed register access macros */
73 WRITE_PORT_UCHAR((PUCHAR
)(ULONG_PTR
)0x22, reg
);
74 return READ_PORT_UCHAR((PUCHAR
)(ULONG_PTR
)0x23);
79 setCx86(UCHAR reg
, UCHAR data
)
81 WRITE_PORT_UCHAR((PUCHAR
)(ULONG_PTR
)0x22, reg
);
82 WRITE_PORT_UCHAR((PUCHAR
)(ULONG_PTR
)0x23, data
);
86 /* FUNCTIONS *****************************************************************/
91 KiSetProcessorType(VOID
)
93 ULONG EFlags
, NewEFlags
;
97 /* Start by assuming no CPUID data */
98 KeGetCurrentPrcb()->CpuID
= 0;
101 EFlags
= __readeflags();
103 /* XOR out the ID bit and update EFlags */
104 NewEFlags
= EFlags
^ EFLAGS_ID
;
105 __writeeflags(NewEFlags
);
107 /* Get them back and see if they were modified */
108 NewEFlags
= __readeflags();
109 if (NewEFlags
!= EFlags
)
111 /* The modification worked, so CPUID exists. Set the ID Bit again. */
113 __writeeflags(EFlags
);
115 /* Peform CPUID 0 to see if CPUID 1 is supported */
116 KiCpuId(&CpuInfo
, 0);
120 KiCpuId(&CpuInfo
, 1);
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.
127 * For the stepping, we convert this: zzzzzzxy into this: x0y
129 Stepping
= CpuInfo
.Eax
& 0xF0;
131 Stepping
+= (CpuInfo
.Eax
& 0xFF);
133 Type
= CpuInfo
.Eax
& 0xF00;
136 /* Save them in the PRCB */
137 KeGetCurrentPrcb()->CpuID
= TRUE
;
138 KeGetCurrentPrcb()->CpuType
= (UCHAR
)Type
;
139 KeGetCurrentPrcb()->CpuStep
= (USHORT
)Stepping
;
143 DPRINT1("CPUID Support lacking\n");
148 DPRINT1("CPUID Support lacking\n");
152 __writeeflags(EFlags
);
160 PKPRCB Prcb
= KeGetCurrentPrcb();
163 /* Assume no Vendor ID and fail if no CPUID Support. */
164 Prcb
->VendorString
[0] = 0;
165 if (!Prcb
->CpuID
) return 0;
167 /* Get the Vendor ID */
168 KiCpuId(&CpuInfo
, 0);
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;
176 /* Now check the CPU Type */
177 if (!strcmp(Prcb
->VendorString
, CmpIntelID
))
181 else if (!strcmp(Prcb
->VendorString
, CmpAmdID
))
185 else if (!strcmp(Prcb
->VendorString
, CmpCyrixID
))
187 DPRINT1("Cyrix CPU support not fully tested!\n");
190 else if (!strcmp(Prcb
->VendorString
, CmpTransmetaID
))
192 DPRINT1("Transmeta CPU support not fully tested!\n");
193 return CPU_TRANSMETA
;
195 else if (!strcmp(Prcb
->VendorString
, CmpCentaurID
))
197 DPRINT1("Centaur CPU support not fully tested!\n");
200 else if (!strcmp(Prcb
->VendorString
, CmpRiseID
))
202 DPRINT1("Rise CPU support not fully tested!\n");
207 DPRINT1("%s CPU support not fully tested!\n", Prcb
->VendorString
);
214 KiGetFeatureBits(VOID
)
216 PKPRCB Prcb
= KeGetCurrentPrcb();
218 ULONG FeatureBits
= KF_WORKING_PTE
;
219 CPU_INFO CpuInfo
, DummyCpuInfo
;
221 BOOLEAN ExtendedCPUID
= TRUE
;
222 ULONG CpuFeatures
= 0;
224 /* Get the Vendor ID */
225 Vendor
= KiGetCpuVendor();
227 /* Make sure we got a valid vendor ID at least. */
228 if (!Vendor
) return FeatureBits
;
230 /* Get the CPUID Info. Features are in Reg[3]. */
231 KiCpuId(&CpuInfo
, 1);
233 /* Set the initial APIC ID */
234 Prcb
->InitialApicId
= (UCHAR
)(CpuInfo
.Ebx
>> 24);
241 /* Check if it's a P6 */
242 if (Prcb
->CpuType
== 6)
244 /* Perform the special sequence to get the MicroCode Signature */
246 KiCpuId(&DummyCpuInfo
, 1);
247 Prcb
->UpdateSignature
.QuadPart
= __readmsr(0x8B);
249 else if (Prcb
->CpuType
== 5)
251 /* On P5, enable workaround for the LOCK errata. */
252 KiI386PentiumLockErrataPresent
= TRUE
;
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))
259 /* Remove support for correct PTE support. */
260 FeatureBits
&= ~KF_WORKING_PTE
;
263 /* Check if the CPU is too old to support SYSENTER */
264 if ((Prcb
->CpuType
< 6) ||
265 ((Prcb
->CpuType
== 6) && (Prcb
->CpuStep
< 0x0303)))
268 CpuInfo
.Edx
&= ~0x800;
276 /* Check if this is a K5 or K6. (family 5) */
277 if ((CpuInfo
.Eax
& 0x0F00) == 0x0500)
279 /* Get the Model Number */
280 switch (CpuInfo
.Eax
& 0x00F0)
282 /* Model 1: K5 - 5k86 (initial models) */
285 /* Check if this is Step 0 or 1. They don't support PGE */
286 if ((CpuInfo
.Eax
& 0x000F) > 0x03) break;
288 /* Model 0: K5 - SSA5 */
291 /* Model 0 doesn't support PGE at all. */
292 CpuInfo
.Edx
&= ~0x2000;
298 /* K6-2, Step 8 and over have support for MTRR. */
299 if ((CpuInfo
.Eax
& 0x000F) >= 0x8) FeatureBits
|= KF_AMDK6MTRR
;
303 Model D: K6-2+, K6-III+ */
307 FeatureBits
|= KF_AMDK6MTRR
;
311 else if((CpuInfo
.Eax
& 0x0F00) < 0x0500)
313 /* Families below 5 don't support PGE, PSE or CMOV at all */
314 CpuInfo
.Edx
&= ~(0x08 | 0x2000 | 0x8000);
316 /* They also don't support advanced CPUID functions. */
317 ExtendedCPUID
= FALSE
;
325 /* Workaround the "COMA" bug on 6x family of Cyrix CPUs */
326 if (Prcb
->CpuType
== 6 &&
330 Ccr1
= getCx86(CX86_CCR1
);
332 /* Enable the NO_LOCK bit */
335 /* Set the new CCR1 value */
336 setCx86(CX86_CCR1
, Ccr1
);
344 /* Enable CMPXCHG8B if the family (>= 5), model and stepping (>= 4.2) support it */
345 if ((CpuInfo
.Eax
& 0x0FFF) >= 0x0542)
347 __writemsr(0x80860004, __readmsr(0x80860004) | 0x0100);
348 FeatureBits
|= KF_CMPXCHG8B
;
353 /* Centaur, IDT, Rise and VIA CPUs */
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
;
364 /* Set the current features */
365 CpuFeatures
= CpuInfo
.Edx
;
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
;
383 /* Check if the CPU has hyper-threading */
384 if (CpuFeatures
& 0x10000000)
386 /* Set the number of logical CPUs */
387 Prcb
->LogicalProcessorsPerPhysicalProcessor
= (UCHAR
)(CpuInfo
.Ebx
>> 16);
388 if (Prcb
->LogicalProcessorsPerPhysicalProcessor
> 1)
390 /* We're on dual-core */
391 KiSMTProcessorsPresent
= TRUE
;
396 /* We only have a single CPU */
397 Prcb
->LogicalProcessorsPerPhysicalProcessor
= 1;
400 /* Check if CPUID 0x80000000 is supported */
404 KiCpuId(&CpuInfo
, 0x80000000);
405 if ((CpuInfo
.Eax
& 0xffffff00) == 0x80000000)
407 /* Check if CPUID 0x80000001 is supported */
408 if (CpuInfo
.Eax
>= 0x80000001)
410 /* Check which extended features are available. */
411 KiCpuId(&CpuInfo
, 0x80000001);
413 /* Check if NX-bit is supported */
414 if (CpuInfo
.Edx
& 0x00100000) FeatureBits
|= KF_NX_BIT
;
416 /* Now handle each features for each CPU Vendor */
421 if (CpuInfo
.Edx
& 0x80000000) FeatureBits
|= KF_3DNOW
;
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
453 /* Return the Feature Bits */
460 KiGetCacheInformation(VOID
)
462 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
465 ULONG CacheRequests
= 0, i
;
466 ULONG CurrentRegister
;
467 UCHAR RegisterByte
, Associativity
= 0;
468 ULONG Size
, CacheLine
= 64, CurrentSize
= 0;
469 BOOLEAN FirstPass
= TRUE
;
471 /* Set default L2 size */
472 Pcr
->SecondLevelCacheSize
= 0;
474 /* Get the Vendor ID and make sure we support CPUID */
475 Vendor
= KiGetCpuVendor();
478 /* Check the Vendor ID */
481 /* Handle Intel case */
484 /*Check if we support CPUID 2 */
485 KiCpuId(&CpuInfo
, 0);
486 if (CpuInfo
.Eax
>= 2)
488 /* We need to loop for the number of times CPUID will tell us to */
491 /* Do the CPUID call */
492 KiCpuId(&CpuInfo
, 2);
494 /* Check if it was the first call */
498 * The number of times to loop is the first byte. Read
499 * it and then destroy it so we don't get confused.
501 CacheRequests
= CpuInfo
.Eax
& 0xFF;
502 CpuInfo
.Eax
&= 0xFFFFFF00;
504 /* Don't go over this again */
508 /* Loop all 4 registers */
509 for (i
= 0; i
< 4; i
++)
511 /* Get the current register */
512 CurrentRegister
= CpuInfo
.AsUINT32
[i
];
515 * If the upper bit is set, then this register should
518 if (CurrentRegister
& 0x80000000) continue;
520 /* Keep looping for every byte inside this register */
521 while (CurrentRegister
)
523 /* Read a byte, skip a byte. */
524 RegisterByte
= (UCHAR
)(CurrentRegister
& 0xFF);
525 CurrentRegister
>>= 8;
526 if (!RegisterByte
) continue;
529 * Valid values are from 0x40 (0 bytes) to 0x49
530 * (32MB), or from 0x80 to 0x89 (same size but
533 if (((RegisterByte
> 0x40) && (RegisterByte
<= 0x47)) ||
534 ((RegisterByte
> 0x78) && (RegisterByte
<= 0x7C)) ||
535 ((RegisterByte
> 0x80) && (RegisterByte
<= 0x85)))
537 /* Compute associativity */
539 if (RegisterByte
>= 0x79) Associativity
= 8;
541 /* Mask out only the first nibble */
542 RegisterByte
&= 0x07;
544 /* Check if this cache is bigger than the last */
545 Size
= 0x10000 << RegisterByte
;
546 if ((Size
/ Associativity
) > CurrentSize
)
548 /* Set the L2 Cache Size and Associativity */
549 CurrentSize
= Size
/ Associativity
;
550 Pcr
->SecondLevelCacheSize
= Size
;
551 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
554 else if ((RegisterByte
> 0x21) && (RegisterByte
<= 0x29))
556 /* Set minimum cache line size */
557 if (CacheLine
< 128) CacheLine
= 128;
559 /* Hard-code size/associativity */
561 switch (RegisterByte
)
585 /* Check if this cache is bigger than the last */
586 if ((Size
/ Associativity
) > CurrentSize
)
588 /* Set the L2 Cache Size and Associativity */
589 CurrentSize
= Size
/ Associativity
;
590 Pcr
->SecondLevelCacheSize
= Size
;
591 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
594 else if (((RegisterByte
> 0x65) && (RegisterByte
< 0x69)) ||
595 (RegisterByte
== 0x2C) || (RegisterByte
== 0xF0))
597 /* Indicates L1 cache line of 64 bytes */
598 KePrefetchNTAGranularity
= 64;
600 else if (RegisterByte
== 0xF1)
602 /* Indicates L1 cache line of 128 bytes */
603 KePrefetchNTAGranularity
= 128;
605 else if (((RegisterByte
>= 0x4A) && (RegisterByte
<= 0x4C)) ||
606 (RegisterByte
== 0x78) ||
607 (RegisterByte
== 0x7D) ||
608 (RegisterByte
== 0x7F) ||
609 (RegisterByte
== 0x86) ||
610 (RegisterByte
== 0x87))
612 /* Set minimum cache line size */
613 if (CacheLine
< 64) CacheLine
= 64;
615 /* Hard-code size/associativity */
616 switch (RegisterByte
)
619 Size
= 4 * 1024 * 1024;
624 Size
= 6 * 1024 * 1024;
629 Size
= 8 * 1024 * 1024;
634 Size
= 1 * 1024 * 1024;
639 Size
= 2 * 1024 * 1024;
654 Size
= 1 * 1024 * 1024;
663 /* Check if this cache is bigger than the last */
664 if ((Size
/ Associativity
) > CurrentSize
)
666 /* Set the L2 Cache Size and Associativity */
667 CurrentSize
= Size
/ Associativity
;
668 Pcr
->SecondLevelCacheSize
= Size
;
669 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
674 } while (--CacheRequests
);
680 /* Check if we support CPUID 0x80000005 */
681 KiCpuId(&CpuInfo
, 0x80000000);
682 if (CpuInfo
.Eax
>= 0x80000006)
684 /* Get L1 size first */
685 KiCpuId(&CpuInfo
, 0x80000005);
686 KePrefetchNTAGranularity
= CpuInfo
.Ecx
& 0xFF;
688 /* Check if we support CPUID 0x80000006 */
689 KiCpuId(&CpuInfo
, 0x80000000);
690 if (CpuInfo
.Eax
>= 0x80000006)
692 /* Get 2nd level cache and tlb size */
693 KiCpuId(&CpuInfo
, 0x80000006);
695 /* Cache line size */
696 CacheLine
= CpuInfo
.Ecx
& 0xFF;
698 /* Hardcode associativity */
699 RegisterByte
= (CpuInfo
.Ecx
>> 12) & 0xFF;
700 switch (RegisterByte
)
725 Size
= (CpuInfo
.Ecx
>> 16) << 10;
727 /* Hack for Model 6, Steping 300 */
728 if ((KeGetCurrentPrcb()->CpuType
== 6) &&
729 (KeGetCurrentPrcb()->CpuStep
== 0x300))
731 /* Stick 64K in there */
735 /* Set the L2 Cache Size and associativity */
736 Pcr
->SecondLevelCacheSize
= Size
;
737 Pcr
->SecondLevelCacheAssociativity
= Associativity
;
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
,
757 Pcr
->SecondLevelCacheAssociativity
);
767 /* Save current CR0 */
770 /* If this is a 486, enable Write-Protection */
771 if (KeGetCurrentPrcb()->CpuType
> 3) Cr0
|= CR0_WP
;
780 KiInitializeTSS2(IN PKTSS Tss
,
781 IN PKGDTENTRY TssEntry OPTIONAL
)
785 /* Make sure the GDT Entry is valid */
789 TssEntry
->LimitLow
= sizeof(KTSS
) - 1;
790 TssEntry
->HighWord
.Bits
.LimitHi
= 0;
793 /* Now clear the I/O Map */
794 ASSERT(IOPM_COUNT
== 1);
795 RtlFillMemory(Tss
->IoMaps
[0].IoMap
, IOPM_FULL_SIZE
, 0xFF);
797 /* Initialize Interrupt Direction Maps */
798 p
= (PUCHAR
)(Tss
->IoMaps
[0].DirectionMap
);
799 RtlZeroMemory(p
, IOPM_DIRECTION_MAP_SIZE
);
801 /* Add DPMI support for interrupts */
806 /* Initialize the default Interrupt Direction Map */
807 p
= Tss
->IntDirectionMap
;
808 RtlZeroMemory(Tss
->IntDirectionMap
, IOPM_DIRECTION_MAP_SIZE
);
810 /* Add DPMI support */
818 KiInitializeTSS(IN PKTSS Tss
)
820 /* Set an invalid map base */
821 Tss
->IoMapBase
= KiComputeIopmOffset(IO_ACCESS_MAP_NONE
);
823 /* Disable traps during Task Switches */
826 /* Set LDT and Ring 0 SS */
828 Tss
->Ss0
= KGDT_R0_DATA
;
834 Ki386InitializeTss(IN PKTSS Tss
,
838 PKGDTENTRY TssEntry
, TaskGateEntry
;
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
);
848 /* Load the task register */
849 Ke386SetTr(KGDT_TSS
);
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
;
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
;
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
;
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
;
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
;
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
;
914 KeFlushCurrentTb(VOID
)
916 /* Flush the TLB by resetting CR3 */
917 __writecr3(__readcr3());
922 KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState
)
927 // Restore the CR registers
929 __writecr0(ProcessorState
->SpecialRegisters
.Cr0
);
930 Ke386SetCr2(ProcessorState
->SpecialRegisters
.Cr2
);
931 __writecr3(ProcessorState
->SpecialRegisters
.Cr3
);
932 if (KeFeatureBits
& KF_CR4
) __writecr4(ProcessorState
->SpecialRegisters
.Cr4
);
935 // Restore the DR registers
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
);
945 // Restore GDT and IDT
947 Ke386SetGlobalDescriptorTable(&ProcessorState
->SpecialRegisters
.Gdtr
.Limit
);
948 __lidt(&ProcessorState
->SpecialRegisters
.Idtr
.Limit
);
951 // Clear the busy flag so we don't crash if we reload the same selector
953 TssEntry
= (PKGDTENTRY
)(ProcessorState
->SpecialRegisters
.Gdtr
.Base
+
954 ProcessorState
->SpecialRegisters
.Tr
);
955 TssEntry
->HighWord
.Bytes
.Flags1
&= ~0x2;
958 // Restore TSS and LDT
960 Ke386SetTr(ProcessorState
->SpecialRegisters
.Tr
);
961 Ke386SetLocalDescriptorTable(ProcessorState
->SpecialRegisters
.Ldtr
);
966 KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState
)
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
) ?
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);
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();
994 KiInitializeMachineType(VOID
)
996 /* Set the Machine Type we got from NTLDR */
997 KeI386MachineType
= KeLoaderBlock
->u
.I386
.MachineType
& 0x000FF;
1003 KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context
)
1005 /* Set CS and ESP */
1006 __writemsr(0x174, KGDT_R0_CODE
);
1007 __writemsr(0x175, (ULONG_PTR
)KeGetCurrentPrcb()->DpcStack
);
1010 __writemsr(0x176, (ULONG_PTR
)KiFastCallEntry
);
1017 KiRestoreFastSyscallReturnState(VOID
)
1019 /* Check if the CPU Supports fast system call */
1020 if (KeFeatureBits
& KF_FAST_SYSCALL
)
1022 /* Check if it has been disabled */
1023 if (KiFastSystemCallDisable
)
1025 /* Disable fast system call */
1026 KeFeatureBits
&= ~KF_FAST_SYSCALL
;
1027 KiFastCallExitHandler
= KiSystemCallTrapReturn
;
1028 DPRINT1("Support for SYSENTER disabled.\n");
1032 /* Do an IPI to enable it */
1033 KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters
, 0);
1035 /* It's enabled, so use the proper exit stub */
1036 KiFastCallExitHandler
= KiSystemCallSysExitReturn
;
1037 DPRINT("Support for SYSENTER detected.\n");
1042 /* Use the IRET handler */
1043 KiFastCallExitHandler
= KiSystemCallTrapReturn
;
1044 DPRINT1("No support for SYSENTER detected.\n");
1051 Ki386EnableDE(IN ULONG_PTR Context
)
1054 __writecr4(__readcr4() | CR4_DE
);
1061 Ki386EnableFxsr(IN ULONG_PTR Context
)
1064 __writecr4(__readcr4() | CR4_FXSR
);
1071 Ki386EnableXMMIExceptions(IN ULONG_PTR Context
)
1073 PKIDTENTRY IdtEntry
;
1075 /* Get the IDT Entry for Interrupt 0x13 */
1076 IdtEntry
= &((PKIPCR
)KeGetPcr())->IDT
[0x13];
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
;
1086 /* Enable XMMI exceptions */
1087 __writecr4(__readcr4() | CR4_XMMEXCPT
);
1094 KiI386PentiumLockErrataFixup(VOID
)
1096 KDESCRIPTOR IdtDescriptor
= {0, 0, 0};
1097 PKIDTENTRY NewIdt
, NewIdt2
;
1099 /* Allocate memory for a new IDT */
1100 NewIdt
= ExAllocatePool(NonPagedPool
, 2 * PAGE_SIZE
);
1102 /* Put everything after the first 7 entries on a new page */
1103 NewIdt2
= (PVOID
)((ULONG_PTR
)NewIdt
+ PAGE_SIZE
- (7 * sizeof(KIDTENTRY
)));
1105 /* Disable interrupts */
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
;
1115 /* Set the new IDT */
1116 __lidt(&IdtDescriptor
.Limit
);
1117 ((PKIPCR
)KeGetPcr())->IDT
= NewIdt2
;
1119 /* Restore interrupts */
1122 /* Set the first 7 entries as read-only to produce a fault */
1123 MmSetPageProtect(NULL
, NewIdt
, PAGE_READONLY
);
1128 KeInvalidateAllCaches(VOID
)
1130 /* Only supported on Pentium Pro and higher */
1131 if (KeI386CpuType
< 6) return FALSE
;
1133 /* Invalidate all caches */
1140 KeZeroPages(IN PVOID Address
,
1143 /* Not using XMMI in this routine */
1144 RtlZeroMemory(Address
, Size
);
1149 KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame
,
1150 IN PKEXCEPTION_FRAME ExceptionFrame
)
1152 PKPRCB Prcb
= KeGetCurrentPrcb();
1155 // Save full context
1157 Prcb
->ProcessorState
.ContextFrame
.ContextFlags
= CONTEXT_FULL
|
1158 CONTEXT_DEBUG_REGISTERS
;
1159 KeTrapFrameToContext(TrapFrame
, NULL
, &Prcb
->ProcessorState
.ContextFrame
);
1162 // Save control registers
1164 KiSaveProcessorControlState(&Prcb
->ProcessorState
);
1170 KiIsNpxPresent(VOID
)
1178 /* Read CR0 and mask out FPU flags */
1179 Cr0
= __readcr0() & ~(CR0_MP
| CR0_TS
| CR0_EM
| CR0_ET
);
1181 /* Store on FPU stack */
1186 asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic
));
1189 /* Magic should now be cleared */
1192 /* You don't have an FPU -- enable emulation for now */
1193 __writecr0(Cr0
| CR0_EM
| CR0_TS
);
1197 /* You have an FPU, enable it */
1200 /* Enable INT 16 on 486 and higher */
1201 if (KeGetCurrentPrcb()->CpuType
>= 3) Cr0
|= CR0_NE
;
1204 __writecr0(Cr0
| CR0_EM
| CR0_TS
);
1211 KiIsNpxErrataPresent(VOID
)
1213 static double Value1
= 4195835.0, Value2
= 3145727.0;
1217 /* Disable interrupts */
1220 /* Read CR0 and remove FPU flags */
1222 __writecr0(Cr0
& ~(CR0_MP
| CR0_TS
| CR0_EM
));
1224 /* Initialize FPU state */
1227 /* Multiply the magic values and divide, we should get the result back */
1229 __asm__ __volatile__
1237 : "=m" (ErrataPresent
)
1256 /* Enable interrupts */
1259 /* Return if there's an errata */
1260 return ErrataPresent
!= 0;
1265 KiFlushNPXState(IN PFLOATING_SAVE_AREA SaveArea
)
1268 PKTHREAD Thread
, NpxThread
;
1269 PFX_SAVE_AREA FxSaveArea
;
1271 /* Save volatiles and disable interrupts */
1272 EFlags
= __readeflags();
1275 /* Save the PCR and get the current thread */
1276 Thread
= KeGetCurrentThread();
1278 /* Check if we're already loaded */
1279 if (Thread
->NpxState
!= NPX_STATE_LOADED
)
1281 /* If there's nothing to load, quit */
1284 /* Restore interrupt state and return */
1285 __writeeflags(EFlags
);
1289 /* Need FXSR support for this */
1290 ASSERT(KeI386FxsrPresent
== TRUE
);
1292 /* Check for sane CR0 */
1294 if (Cr0
& (CR0_MP
| CR0_TS
| CR0_EM
))
1296 /* Mask out FPU flags */
1297 __writecr0(Cr0
& ~(CR0_MP
| CR0_TS
| CR0_EM
));
1300 /* Get the NPX thread and check its FPU state */
1301 NpxThread
= KeGetCurrentPrcb()->NpxThread
;
1302 if ((NpxThread
) && (NpxThread
->NpxState
== NPX_STATE_LOADED
))
1304 /* Get the FX frame and store the state there */
1305 FxSaveArea
= KiGetThreadNpxArea(NpxThread
);
1306 Ke386FxSave(FxSaveArea
);
1308 /* NPX thread has lost its state */
1309 NpxThread
->NpxState
= NPX_STATE_NOT_LOADED
;
1312 /* Now load NPX state from the NPX area */
1313 FxSaveArea
= KiGetThreadNpxArea(Thread
);
1314 Ke386FxStore(FxSaveArea
);
1318 /* Check for sane CR0 */
1320 if (Cr0
& (CR0_MP
| CR0_TS
| CR0_EM
))
1322 /* Mask out FPU flags */
1323 __writecr0(Cr0
& ~(CR0_MP
| CR0_TS
| CR0_EM
));
1327 FxSaveArea
= KiGetThreadNpxArea(Thread
);
1328 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
1330 /* Save state if supported by CPU */
1331 if (KeI386FxsrPresent
) Ke386FxSave(FxSaveArea
);
1334 /* Now save the FN state wherever it was requested */
1335 if (SaveArea
) Ke386FnSave(SaveArea
);
1337 /* Clear NPX thread */
1338 KeGetCurrentPrcb()->NpxThread
= NULL
;
1340 /* Add the CR0 from the NPX frame */
1341 Cr0
|= NPX_STATE_NOT_LOADED
;
1342 Cr0
|= FxSaveArea
->Cr0NpxState
;
1345 /* Restore interrupt state */
1346 __writeeflags(EFlags
);
1349 /* PUBLIC FUNCTIONS **********************************************************/
1356 KiCoprocessorError(VOID
)
1358 PFX_SAVE_AREA NpxArea
;
1360 /* Get the FPU area */
1361 NpxArea
= KiGetThreadNpxArea(KeGetCurrentThread());
1364 NpxArea
->Cr0NpxState
= CR0_TS
;
1365 __writecr0(__readcr0() | CR0_TS
);
1373 KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save
)
1375 PFNSAVE_FORMAT FpState
;
1376 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
1377 DPRINT1("%s is not really implemented\n", __FUNCTION__
);
1379 /* check if we are doing software emulation */
1380 if (!KeI386NpxPresent
) return STATUS_ILLEGAL_FLOAT_CONTEXT
;
1382 FpState
= ExAllocatePool(NonPagedPool
, sizeof (FNSAVE_FORMAT
));
1383 if (!FpState
) return STATUS_INSUFFICIENT_RESOURCES
;
1385 *((PVOID
*) Save
) = FpState
;
1387 asm volatile("fnsave %0\n\t" : "=m" (*FpState
));
1396 KeGetCurrentThread()->Header
.NpxIrql
= KeGetCurrentIrql();
1397 return STATUS_SUCCESS
;
1405 KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save
)
1407 PFNSAVE_FORMAT FpState
= *((PVOID
*) Save
);
1408 ASSERT(KeGetCurrentThread()->Header
.NpxIrql
== KeGetCurrentIrql());
1409 DPRINT1("%s is not really implemented\n", __FUNCTION__
);
1412 asm volatile("fnclex\n\t");
1413 asm volatile("frstor %0\n\t" : "=m" (*FpState
));
1423 ExFreePool(FpState
);
1424 return STATUS_SUCCESS
;
1432 KeGetRecommendedSharedDataAlignment(VOID
)
1434 /* Return the global variable */
1435 return KeLargestCacheLine
;
1440 KiFlushTargetEntireTb(IN PKIPI_CONTEXT PacketContext
,
1445 /* Signal this packet as done */
1446 KiIpiSignalPacketDone(PacketContext
);
1448 /* Flush the TB for the Current CPU */
1457 KeFlushEntireTb(IN BOOLEAN Invalid
,
1458 IN BOOLEAN AllProcessors
)
1462 KAFFINITY TargetAffinity
;
1463 PKPRCB Prcb
= KeGetCurrentPrcb();
1466 /* Raise the IRQL for the TB Flush */
1467 OldIrql
= KeRaiseIrqlToSynchLevel();
1470 /* FIXME: Use KiTbFlushTimeStamp to synchronize TB flush */
1472 /* Get the current processor affinity, and exclude ourselves */
1473 TargetAffinity
= KeActiveProcessors
;
1474 TargetAffinity
&= ~Prcb
->SetMember
;
1476 /* Make sure this is MP */
1479 /* Send an IPI TB flush to the other processors */
1480 KiIpiSendPacket(TargetAffinity
,
1481 KiFlushTargetEntireTb
,
1488 /* Flush the TB for the Current CPU, and update the flush stamp */
1492 /* If this is MP, wait for the other processors to finish */
1496 ASSERT(Prcb
== KeGetCurrentPrcb());
1499 ASSERTMSG("Not yet implemented\n", FALSE
);
1503 /* Update the flush stamp and return to original IRQL */
1504 InterlockedExchangeAdd(&KiTbFlushTimeStamp
, 1);
1505 KeLowerIrql(OldIrql
);
1513 KeSetDmaIoCoherency(IN ULONG Coherency
)
1515 /* Save the coherency globally */
1516 KiDmaIoCoherency
= Coherency
;
1524 KeQueryActiveProcessors(VOID
)
1528 /* Simply return the number of active processors */
1529 return KeActiveProcessors
;
1537 KeSaveStateForHibernate(IN PKPROCESSOR_STATE State
)
1539 /* Capture the context */
1540 RtlCaptureContext(&State
->ContextFrame
);
1542 /* Capture the control state */
1543 KiSaveProcessorControlState(State
);