xref: /reactos/ntoskrnl/ke/amd64/cpu.c (revision 9d3c3a75)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/amd64/cpu.c
5  * PURPOSE:         Routines for CPU-level support
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Timo Kreuzer (timo.kreuzer@reactos.org)
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS *******************************************************************/
17 
18 /* The Boot TSS */
19 KTSS64 KiBootTss;
20 
21 /* CPU Features and Flags */
22 ULONG KeI386CpuType;
23 ULONG KeI386CpuStep;
24 ULONG KeI386MachineType;
25 ULONG KeI386NpxPresent = 1;
26 ULONG KeLargestCacheLine = 0x40;
27 ULONG KiDmaIoCoherency = 0;
28 BOOLEAN KiSMTProcessorsPresent;
29 
30 /* Flush data */
31 volatile LONG KiTbFlushTimeStamp;
32 
33 /* CPU Signatures */
34 static const CHAR CmpIntelID[]       = "GenuineIntel";
35 static const CHAR CmpAmdID[]         = "AuthenticAMD";
36 static const CHAR CmpCentaurID[]     = "CentaurHauls";
37 
38 /* FUNCTIONS *****************************************************************/
39 
40 VOID
41 NTAPI
42 KiSetProcessorType(VOID)
43 {
44     CPU_INFO CpuInfo;
45     ULONG Stepping, Type;
46 
47     /* Do CPUID 1 now */
48     KiCpuId(&CpuInfo, 1);
49 
50     /*
51      * Get the Stepping and Type. The stepping contains both the
52      * Model and the Step, while the Type contains the returned Type.
53      * We ignore the family.
54      *
55      * For the stepping, we convert this: zzzzzzxy into this: x0y
56      */
57     Stepping = CpuInfo.Eax & 0xF0;
58     Stepping <<= 4;
59     Stepping += (CpuInfo.Eax & 0xFF);
60     Stepping &= 0xF0F;
61     Type = CpuInfo.Eax & 0xF00;
62     Type >>= 8;
63 
64     /* Save them in the PRCB */
65     KeGetCurrentPrcb()->CpuID = TRUE;
66     KeGetCurrentPrcb()->CpuType = (UCHAR)Type;
67     KeGetCurrentPrcb()->CpuStep = (USHORT)Stepping;
68 }
69 
70 ULONG
71 NTAPI
72 KiGetCpuVendor(VOID)
73 {
74     PKPRCB Prcb = KeGetCurrentPrcb();
75     CPU_INFO CpuInfo;
76 
77     /* Get the Vendor ID and null-terminate it */
78     KiCpuId(&CpuInfo, 0);
79 
80     /* Copy it to the PRCB and null-terminate it */
81     *(ULONG*)&Prcb->VendorString[0] = CpuInfo.Ebx;
82     *(ULONG*)&Prcb->VendorString[4] = CpuInfo.Edx;
83     *(ULONG*)&Prcb->VendorString[8] = CpuInfo.Ecx;
84     Prcb->VendorString[12] = 0;
85 
86     /* Now check the CPU Type */
87     if (!strcmp((PCHAR)Prcb->VendorString, CmpIntelID))
88     {
89         Prcb->CpuVendor = CPU_INTEL;
90     }
91     else if (!strcmp((PCHAR)Prcb->VendorString, CmpAmdID))
92     {
93         Prcb->CpuVendor = CPU_AMD;
94     }
95     else if (!strcmp((PCHAR)Prcb->VendorString, CmpCentaurID))
96     {
97         DPRINT1("VIA CPUs not fully supported\n");
98         Prcb->CpuVendor = CPU_VIA;
99     }
100     else
101     {
102         /* Invalid CPU */
103         DPRINT1("%s CPU support not fully tested!\n", Prcb->VendorString);
104         Prcb->CpuVendor = CPU_UNKNOWN;
105     }
106 
107     return Prcb->CpuVendor;
108 }
109 
110 ULONG
111 NTAPI
112 KiGetFeatureBits(VOID)
113 {
114     PKPRCB Prcb = KeGetCurrentPrcb();
115     ULONG Vendor;
116     ULONG FeatureBits = KF_WORKING_PTE;
117     CPU_INFO CpuInfo;
118 
119     /* Get the Vendor ID */
120     Vendor = KiGetCpuVendor();
121 
122     /* Make sure we got a valid vendor ID at least. */
123     if (!Vendor) return FeatureBits;
124 
125     /* Get the CPUID Info. */
126     KiCpuId(&CpuInfo, 1);
127 
128     /* Set the initial APIC ID */
129     Prcb->InitialApicId = (UCHAR)(CpuInfo.Ebx >> 24);
130 
131     /* Convert all CPUID Feature bits into our format */
132     if (CpuInfo.Edx & X86_FEATURE_VME) FeatureBits |= KF_V86_VIS | KF_CR4;
133     if (CpuInfo.Edx & X86_FEATURE_PSE) FeatureBits |= KF_LARGE_PAGE | KF_CR4;
134     if (CpuInfo.Edx & X86_FEATURE_TSC) FeatureBits |= KF_RDTSC;
135     if (CpuInfo.Edx & X86_FEATURE_CX8) FeatureBits |= KF_CMPXCHG8B;
136     if (CpuInfo.Edx & X86_FEATURE_SYSCALL) FeatureBits |= KF_FAST_SYSCALL;
137     if (CpuInfo.Edx & X86_FEATURE_MTTR) FeatureBits |= KF_MTRR;
138     if (CpuInfo.Edx & X86_FEATURE_PGE) FeatureBits |= KF_GLOBAL_PAGE | KF_CR4;
139     if (CpuInfo.Edx & X86_FEATURE_CMOV) FeatureBits |= KF_CMOV;
140     if (CpuInfo.Edx & X86_FEATURE_PAT) FeatureBits |= KF_PAT;
141     if (CpuInfo.Edx & X86_FEATURE_DS) FeatureBits |= KF_DTS;
142     if (CpuInfo.Edx & X86_FEATURE_MMX) FeatureBits |= KF_MMX;
143     if (CpuInfo.Edx & X86_FEATURE_FXSR) FeatureBits |= KF_FXSR;
144     if (CpuInfo.Edx & X86_FEATURE_SSE) FeatureBits |= KF_XMMI;
145     if (CpuInfo.Edx & X86_FEATURE_SSE2) FeatureBits |= KF_XMMI64;
146 
147     if (CpuInfo.Ecx & X86_FEATURE_SSE3) FeatureBits |= KF_SSE3;
148     //if (CpuInfo.Ecx & X86_FEATURE_MONITOR) FeatureBits |= KF_MONITOR;
149     //if (CpuInfo.Ecx & X86_FEATURE_SSSE3) FeatureBits |= KF_SSE3SUP;
150     if (CpuInfo.Ecx & X86_FEATURE_CX16) FeatureBits |= KF_CMPXCHG16B;
151     //if (CpuInfo.Ecx & X86_FEATURE_SSE41) FeatureBits |= KF_SSE41;
152     //if (CpuInfo.Ecx & X86_FEATURE_POPCNT) FeatureBits |= KF_POPCNT;
153     if (CpuInfo.Ecx & X86_FEATURE_XSAVE) FeatureBits |= KF_XSTATE;
154 
155     /* Check if the CPU has hyper-threading */
156     if (CpuInfo.Edx & X86_FEATURE_HT)
157     {
158         /* Set the number of logical CPUs */
159         Prcb->LogicalProcessorsPerPhysicalProcessor = (UCHAR)(CpuInfo.Ebx >> 16);
160         if (Prcb->LogicalProcessorsPerPhysicalProcessor > 1)
161         {
162             /* We're on dual-core */
163             KiSMTProcessorsPresent = TRUE;
164         }
165     }
166     else
167     {
168         /* We only have a single CPU */
169         Prcb->LogicalProcessorsPerPhysicalProcessor = 1;
170     }
171 
172     /* Check extended cpuid features */
173     KiCpuId(&CpuInfo, 0x80000000);
174     if ((CpuInfo.Eax & 0xffffff00) == 0x80000000)
175     {
176         /* Check if CPUID 0x80000001 is supported */
177         if (CpuInfo.Eax >= 0x80000001)
178         {
179             /* Check which extended features are available. */
180             KiCpuId(&CpuInfo, 0x80000001);
181 
182             /* Check if NX-bit is supported */
183             if (CpuInfo.Edx & X86_FEATURE_NX) FeatureBits |= KF_NX_BIT;
184 
185             /* Now handle each features for each CPU Vendor */
186             switch (Vendor)
187             {
188                 case CPU_AMD:
189                     if (CpuInfo.Edx & 0x80000000) FeatureBits |= KF_3DNOW;
190                     break;
191             }
192         }
193     }
194 
195     /* Return the Feature Bits */
196     return FeatureBits;
197 }
198 
199 VOID
200 NTAPI
201 KiGetCacheInformation(VOID)
202 {
203     PKIPCR Pcr = (PKIPCR)KeGetPcr();
204     ULONG Vendor;
205     ULONG CacheRequests = 0, i;
206     ULONG CurrentRegister;
207     UCHAR RegisterByte;
208     BOOLEAN FirstPass = TRUE;
209     CPU_INFO CpuInfo;
210 
211     /* Set default L2 size */
212     Pcr->SecondLevelCacheSize = 0;
213 
214     /* Get the Vendor ID and make sure we support CPUID */
215     Vendor = KiGetCpuVendor();
216     if (!Vendor) return;
217 
218     /* Check the Vendor ID */
219     switch (Vendor)
220     {
221         /* Handle Intel case */
222         case CPU_INTEL:
223 
224             /*Check if we support CPUID 2 */
225             KiCpuId(&CpuInfo, 0);
226             if (CpuInfo.Eax >= 2)
227             {
228                 /* We need to loop for the number of times CPUID will tell us to */
229                 do
230                 {
231                     /* Do the CPUID call */
232                     KiCpuId(&CpuInfo, 2);
233 
234                     /* Check if it was the first call */
235                     if (FirstPass)
236                     {
237                         /*
238                          * The number of times to loop is the first byte. Read
239                          * it and then destroy it so we don't get confused.
240                          */
241                         CacheRequests = CpuInfo.Eax & 0xFF;
242                         CpuInfo.Eax &= 0xFFFFFF00;
243 
244                         /* Don't go over this again */
245                         FirstPass = FALSE;
246                     }
247 
248                     /* Loop all 4 registers */
249                     for (i = 0; i < 4; i++)
250                     {
251                         /* Get the current register */
252                         CurrentRegister = CpuInfo.AsUINT32[i];
253 
254                         /*
255                          * If the upper bit is set, then this register should
256                          * be skipped.
257                          */
258                         if (CurrentRegister & 0x80000000) continue;
259 
260                         /* Keep looping for every byte inside this register */
261                         while (CurrentRegister)
262                         {
263                             /* Read a byte, skip a byte. */
264                             RegisterByte = (UCHAR)(CurrentRegister & 0xFF);
265                             CurrentRegister >>= 8;
266                             if (!RegisterByte) continue;
267 
268                             /*
269                              * Valid values are from 0x40 (0 bytes) to 0x49
270                              * (32MB), or from 0x80 to 0x89 (same size but
271                              * 8-way associative.
272                              */
273                             if (((RegisterByte > 0x40) &&
274                                  (RegisterByte <= 0x49)) ||
275                                 ((RegisterByte > 0x80) &&
276                                 (RegisterByte <= 0x89)))
277                             {
278                                 /* Mask out only the first nibble */
279                                 RegisterByte &= 0x0F;
280 
281                                 /* Set the L2 Cache Size */
282                                 Pcr->SecondLevelCacheSize = 0x10000 <<
283                                                             RegisterByte;
284                             }
285                         }
286                     }
287                 } while (--CacheRequests);
288             }
289             break;
290 
291         case CPU_AMD:
292 
293             /* Check if we support CPUID 0x80000006 */
294             KiCpuId(&CpuInfo, 0x80000000);
295             if (CpuInfo.Eax >= 6)
296             {
297                 /* Get 2nd level cache and tlb size */
298                 KiCpuId(&CpuInfo, 0x80000006);
299 
300                 /* Set the L2 Cache Size */
301                 Pcr->SecondLevelCacheSize = (CpuInfo.Ecx & 0xFFFF0000) >> 6;
302             }
303             break;
304     }
305 }
306 
307 VOID
308 NTAPI
309 KeFlushCurrentTb(VOID)
310 {
311     /* Flush the TLB by resetting CR3 */
312     __writecr3(__readcr3());
313 }
314 
315 VOID
316 NTAPI
317 KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState)
318 {
319     /* Restore the CR registers */
320     __writecr0(ProcessorState->SpecialRegisters.Cr0);
321 //    __writecr2(ProcessorState->SpecialRegisters.Cr2);
322     __writecr3(ProcessorState->SpecialRegisters.Cr3);
323     __writecr4(ProcessorState->SpecialRegisters.Cr4);
324     __writecr8(ProcessorState->SpecialRegisters.Cr8);
325 
326     /* Restore the DR registers */
327     __writedr(0, ProcessorState->SpecialRegisters.KernelDr0);
328     __writedr(1, ProcessorState->SpecialRegisters.KernelDr1);
329     __writedr(2, ProcessorState->SpecialRegisters.KernelDr2);
330     __writedr(3, ProcessorState->SpecialRegisters.KernelDr3);
331     __writedr(6, ProcessorState->SpecialRegisters.KernelDr6);
332     __writedr(7, ProcessorState->SpecialRegisters.KernelDr7);
333 
334     /* Restore GDT, IDT, LDT and TSS */
335     __lgdt(&ProcessorState->SpecialRegisters.Gdtr.Limit);
336 //    __lldt(&ProcessorState->SpecialRegisters.Ldtr);
337 //    __ltr(&ProcessorState->SpecialRegisters.Tr);
338     __lidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
339 
340 //    __ldmxcsr(&ProcessorState->SpecialRegisters.MxCsr); // FIXME
341 //    ProcessorState->SpecialRegisters.DebugControl
342 //    ProcessorState->SpecialRegisters.LastBranchToRip
343 //    ProcessorState->SpecialRegisters.LastBranchFromRip
344 //    ProcessorState->SpecialRegisters.LastExceptionToRip
345 //    ProcessorState->SpecialRegisters.LastExceptionFromRip
346 
347     /* Restore MSRs */
348     __writemsr(X86_MSR_GSBASE, ProcessorState->SpecialRegisters.MsrGsBase);
349     __writemsr(X86_MSR_KERNEL_GSBASE, ProcessorState->SpecialRegisters.MsrGsSwap);
350     __writemsr(X86_MSR_STAR, ProcessorState->SpecialRegisters.MsrStar);
351     __writemsr(X86_MSR_LSTAR, ProcessorState->SpecialRegisters.MsrLStar);
352     __writemsr(X86_MSR_CSTAR, ProcessorState->SpecialRegisters.MsrCStar);
353     __writemsr(X86_MSR_SFMASK, ProcessorState->SpecialRegisters.MsrSyscallMask);
354 
355 }
356 
357 VOID
358 NTAPI
359 KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState)
360 {
361     /* Save the CR registers */
362     ProcessorState->SpecialRegisters.Cr0 = __readcr0();
363     ProcessorState->SpecialRegisters.Cr2 = __readcr2();
364     ProcessorState->SpecialRegisters.Cr3 = __readcr3();
365     ProcessorState->SpecialRegisters.Cr4 = __readcr4();
366     ProcessorState->SpecialRegisters.Cr8 = __readcr8();
367 
368     /* Save the DR registers */
369     ProcessorState->SpecialRegisters.KernelDr0 = __readdr(0);
370     ProcessorState->SpecialRegisters.KernelDr1 = __readdr(1);
371     ProcessorState->SpecialRegisters.KernelDr2 = __readdr(2);
372     ProcessorState->SpecialRegisters.KernelDr3 = __readdr(3);
373     ProcessorState->SpecialRegisters.KernelDr6 = __readdr(6);
374     ProcessorState->SpecialRegisters.KernelDr7 = __readdr(7);
375 
376     /* Save GDT, IDT, LDT and TSS */
377     __sgdt(&ProcessorState->SpecialRegisters.Gdtr.Limit);
378     __sldt(&ProcessorState->SpecialRegisters.Ldtr);
379     __str(&ProcessorState->SpecialRegisters.Tr);
380     __sidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
381 
382 //    __stmxcsr(&ProcessorState->SpecialRegisters.MxCsr);
383 //    ProcessorState->SpecialRegisters.DebugControl =
384 //    ProcessorState->SpecialRegisters.LastBranchToRip =
385 //    ProcessorState->SpecialRegisters.LastBranchFromRip =
386 //    ProcessorState->SpecialRegisters.LastExceptionToRip =
387 //    ProcessorState->SpecialRegisters.LastExceptionFromRip =
388 
389     /* Save MSRs */
390     ProcessorState->SpecialRegisters.MsrGsBase = __readmsr(X86_MSR_GSBASE);
391     ProcessorState->SpecialRegisters.MsrGsSwap = __readmsr(X86_MSR_KERNEL_GSBASE);
392     ProcessorState->SpecialRegisters.MsrStar = __readmsr(X86_MSR_STAR);
393     ProcessorState->SpecialRegisters.MsrLStar = __readmsr(X86_MSR_LSTAR);
394     ProcessorState->SpecialRegisters.MsrCStar = __readmsr(X86_MSR_CSTAR);
395     ProcessorState->SpecialRegisters.MsrSyscallMask = __readmsr(X86_MSR_SFMASK);
396 }
397 
398 VOID
399 NTAPI
400 KeFlushEntireTb(IN BOOLEAN Invalid,
401                 IN BOOLEAN AllProcessors)
402 {
403     KIRQL OldIrql;
404 
405     // FIXME: halfplemented
406     /* Raise the IRQL for the TB Flush */
407     OldIrql = KeRaiseIrqlToSynchLevel();
408 
409     /* Flush the TB for the Current CPU, and update the flush stamp */
410     KeFlushCurrentTb();
411 
412     /* Update the flush stamp and return to original IRQL */
413     InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1);
414     KeLowerIrql(OldIrql);
415 
416 }
417 
418 KAFFINITY
419 NTAPI
420 KeQueryActiveProcessors(VOID)
421 {
422     PAGED_CODE();
423 
424     /* Simply return the number of active processors */
425     return KeActiveProcessors;
426 }
427 
428 NTSTATUS
429 NTAPI
430 KxSaveFloatingPointState(OUT PKFLOATING_SAVE FloatingState)
431 {
432     UNREFERENCED_PARAMETER(FloatingState);
433     return STATUS_SUCCESS;
434 }
435 
436 NTSTATUS
437 NTAPI
438 KxRestoreFloatingPointState(IN PKFLOATING_SAVE FloatingState)
439 {
440     UNREFERENCED_PARAMETER(FloatingState);
441     return STATUS_SUCCESS;
442 }
443 
444 BOOLEAN
445 NTAPI
446 KeInvalidateAllCaches(VOID)
447 {
448     /* Invalidate all caches */
449     __wbinvd();
450     return TRUE;
451 }
452 
453 /*
454  * @implemented
455  */
456 ULONG
457 NTAPI
458 KeGetRecommendedSharedDataAlignment(VOID)
459 {
460     /* Return the global variable */
461     return KeLargestCacheLine;
462 }
463 
464 /*
465  * @implemented
466  */
467 VOID
468 __cdecl
469 KeSaveStateForHibernate(IN PKPROCESSOR_STATE State)
470 {
471     /* Capture the context */
472     RtlCaptureContext(&State->ContextFrame);
473 
474     /* Capture the control state */
475     KiSaveProcessorControlState(State);
476 }
477 
478 /*
479  * @implemented
480  */
481 VOID
482 NTAPI
483 KeSetDmaIoCoherency(IN ULONG Coherency)
484 {
485     /* Save the coherency globally */
486     KiDmaIoCoherency = Coherency;
487 }
488