- Add KF_XMMI64 feature flag for SSE2 and detect it.
[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 /* FIXME: Local EFLAGS defines not used anywhere else */
16 #define EFLAGS_IOPL 0x3000
17 #define EFLAGS_NF 0x4000
18 #define EFLAGS_RF 0x10000
19 #define EFLAGS_ID 0x200000
20
21 /* GLOBALS *******************************************************************/
22
23 /* The Boot TSS */
24 KTSS KiBootTss;
25
26 /* The TSS to use for Double Fault Traps (INT 0x9) */
27 UCHAR KiDoubleFaultTSS[KTSS_IO_MAPS];
28
29 /* The TSS to use for NMI Fault Traps (INT 0x2) */
30 UCHAR KiNMITSS[KTSS_IO_MAPS];
31
32 /* The Boot GDT (FIXME: should have more entries */
33 KGDTENTRY KiBootGdt[12] =
34 {
35 {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}}, /* KGDT_NULL */
36 {0xffff, 0x0000, {{0x00, 0x9a, 0xcf, 0x00}}}, /* KGDT_R0_CODE */
37 {0xffff, 0x0000, {{0x00, 0x92, 0xcf, 0x00}}}, /* KGDT_R0_DATA */
38 {0xffff, 0x0000, {{0x00, 0xfa, 0xcf, 0x00}}}, /* KGDT_R3_CODE */
39 {0xffff, 0x0000, {{0x00, 0xf2, 0xcf, 0x00}}}, /* KGDT_R3_DATA*/
40 {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}}, /* KGDT_TSS */
41 {0x0fff, 0x0000, {{0x00, 0x92, 0x00, 0xff}}}, /* KGDT_R0_PCR */
42 {0x0fff, 0x0000, {{0x00, 0xf2, 0x00, 0x00}}}, /* KGDT_R3_TEB */
43 {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}}, /* KGDT_UNUSED */
44 {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}}, /* KGDT_LDT */
45 {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}}, /* KGDT_DF_TSS */
46 {0x0000, 0x0000, {{0x00, 0x00, 0x00, 0x00}}} /* KGDT_NMI_TSS */
47 };
48
49 /* GDT Descriptor */
50 KDESCRIPTOR KiGdtDescriptor = {sizeof(KiBootGdt), (ULONG)KiBootGdt};
51
52 /* CPU Features and Flags */
53 ULONG KeI386CpuType;
54 ULONG KeI386CpuStep;
55 ULONG KeProcessorArchitecture;
56 ULONG KeProcessorLevel;
57 ULONG KeProcessorRevision;
58 ULONG KeFeatureBits;
59 ULONG KiFastSystemCallDisable = 1;
60 ULONG KeI386NpxPresent = 0;
61 ULONG KiMXCsrMask = 0;
62 ULONG MxcsrFeatureMask = 0;
63 ULONG KeI386XMMIPresent = 0;
64 ULONG KeI386FxsrPresent = 0;
65 ULONG KeI386MachineType;
66 ULONG Ke386Pae = FALSE;
67 ULONG Ke386NoExecute = FALSE;
68 ULONG KeLargestCacheLine = 0x40;
69 ULONG KeDcacheFlushCount = 0;
70 ULONG KeIcacheFlushCount = 0;
71 ULONG KiDmaIoCoherency = 0;
72 CHAR KeNumberProcessors;
73 KAFFINITY KeActiveProcessors = 1;
74 BOOLEAN KiI386PentiumLockErrataPresent;
75 BOOLEAN KiSMTProcessorsPresent;
76
77 /* CPU Signatures */
78 CHAR CmpIntelID[] = "GenuineIntel";
79 CHAR CmpAmdID[] = "AuthenticAMD";
80 CHAR CmpCyrixID[] = "CyrixInstead";
81 CHAR CmpTransmetaID[] = "GenuineTMx86";
82 CHAR CmpCentaurID[] = "CentaurHauls";
83 CHAR CmpRiseID[] = "RiseRiseRise";
84
85 /* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/
86
87 VOID
88 NTAPI
89 CPUID(IN ULONG CpuInfo[4],
90 IN ULONG InfoType)
91 {
92 Ki386Cpuid(InfoType, &CpuInfo[0], &CpuInfo[1], &CpuInfo[2], &CpuInfo[3]);
93 }
94
95 VOID
96 WRMSR(IN ULONG Register,
97 IN LONGLONG Value)
98 {
99 LARGE_INTEGER LargeVal;
100 LargeVal.QuadPart = Value;
101 Ke386Wrmsr(Register, LargeVal.HighPart, LargeVal.LowPart);
102 }
103
104 LONGLONG
105 RDMSR(IN ULONG Register)
106 {
107 LARGE_INTEGER LargeVal;
108 Ke386Rdmsr(Register, LargeVal.HighPart, LargeVal.LowPart);
109 return LargeVal.QuadPart;
110 }
111
112 /* FUNCTIONS *****************************************************************/
113
114 VOID
115 NTAPI
116 KiSetProcessorType(VOID)
117 {
118 ULONG EFlags, NewEFlags;
119 ULONG Reg[4];
120 ULONG Stepping, Type;
121
122 /* Start by assuming no CPUID data */
123 KeGetCurrentPrcb()->CpuID = 0;
124
125 /* Save EFlags */
126 Ke386SaveFlags(EFlags);
127
128 /* XOR out the ID bit and update EFlags */
129 NewEFlags = EFlags ^ EFLAGS_ID;
130 Ke386RestoreFlags(NewEFlags);
131
132 /* Get them back and see if they were modified */
133 Ke386SaveFlags(NewEFlags);
134 if (NewEFlags != EFlags)
135 {
136 /* The modification worked, so CPUID exists. Set the ID Bit again. */
137 EFlags |= EFLAGS_ID;
138 Ke386RestoreFlags(EFlags);
139
140 /* Peform CPUID 0 to see if CPUID 1 is supported */
141 CPUID(Reg, 0);
142 if (Reg[0] > 0)
143 {
144 /* Do CPUID 1 now */
145 CPUID(Reg, 1);
146
147 /*
148 * Get the Stepping and Type. The stepping contains both the
149 * Model and the Step, while the Type contains the returned Type.
150 * We ignore the family.
151 *
152 * For the stepping, we convert this: zzzzzzxy into this: x0y
153 */
154 Stepping = Reg[0] & 0xF0;
155 Stepping <<= 4;
156 Stepping += (Reg[0] & 0xFF);
157 Stepping &= 0xF0F;
158 Type = Reg[0] & 0xF00;
159 Type >>= 8;
160
161 /* Save them in the PRCB */
162 KeGetCurrentPrcb()->CpuID = TRUE;
163 KeGetCurrentPrcb()->CpuType = (UCHAR)Type;
164 KeGetCurrentPrcb()->CpuStep = (USHORT)Stepping;
165 }
166 else
167 {
168 DPRINT1("CPUID Support lacking\n");
169 }
170 }
171 else
172 {
173 DPRINT1("CPUID Support lacking\n");
174 }
175
176 /* Restore EFLAGS */
177 Ke386RestoreFlags(EFlags);
178 }
179
180 ULONG
181 NTAPI
182 KiGetCpuVendor(VOID)
183 {
184 PKPRCB Prcb = KeGetCurrentPrcb();
185 ULONG Vendor[5];
186
187 /* Assume no Vendor ID and fail if no CPUID Support. */
188 Prcb->VendorString[0] = 0;
189 if (!Prcb->CpuID) return 0;
190
191 /* Get the Vendor ID and null-terminate it */
192 CPUID(Vendor, 0);
193 Vendor[4] = 0;
194
195 /* Re-arrange vendor string */
196 Vendor[5] = Vendor[2];
197 Vendor[2] = Vendor[3];
198 Vendor[3] = Vendor[5];
199
200 /* Copy it to the PRCB and null-terminate it again */
201 RtlCopyMemory(Prcb->VendorString,
202 &Vendor[1],
203 sizeof(Prcb->VendorString) - sizeof(CHAR));
204 Prcb->VendorString[sizeof(Prcb->VendorString) - sizeof(CHAR)] = ANSI_NULL;
205
206 /* Now check the CPU Type */
207 if (!strcmp(Prcb->VendorString, CmpIntelID))
208 {
209 return CPU_INTEL;
210 }
211 else if (!strcmp(Prcb->VendorString, CmpAmdID))
212 {
213 return CPU_AMD;
214 }
215 else if (!strcmp(Prcb->VendorString, CmpCyrixID))
216 {
217 DPRINT1("Cyrix CPUs not fully supported\n");
218 return 0;
219 }
220 else if (!strcmp(Prcb->VendorString, CmpTransmetaID))
221 {
222 DPRINT1("Transmeta CPUs not fully supported\n");
223 return 0;
224 }
225 else if (!strcmp(Prcb->VendorString, CmpCentaurID))
226 {
227 DPRINT1("VIA CPUs not fully supported\n");
228 return 0;
229 }
230 else if (!strcmp(Prcb->VendorString, CmpRiseID))
231 {
232 DPRINT1("Rise CPUs not fully supported\n");
233 return 0;
234 }
235
236 /* Invalid CPU */
237 return 0;
238 }
239
240 ULONG
241 NTAPI
242 KiGetFeatureBits(VOID)
243 {
244 PKPRCB Prcb = KeGetCurrentPrcb();
245 ULONG Vendor;
246 ULONG FeatureBits = KF_WORKING_PTE;
247 ULONG Reg[4];
248 BOOLEAN ExtendedCPUID = TRUE;
249 ULONG CpuFeatures = 0;
250
251 /* Get the Vendor ID */
252 Vendor = KiGetCpuVendor();
253
254 /* Make sure we got a valid vendor ID at least. */
255 if (!Vendor) return FeatureBits;
256
257 /* Get the CPUID Info. Features are in Reg[3]. */
258 CPUID(Reg, 1);
259
260 /* Set the initial APIC ID */
261 Prcb->InitialApicId = (UCHAR)(Reg[1] >> 24);
262
263 /* Check for AMD CPU */
264 if (Vendor == CPU_AMD)
265 {
266 /* Check if this is a K5 or higher. */
267 if ((Reg[0] & 0x0F00) >= 0x0500)
268 {
269 /* Check if this is a K5 specifically. */
270 if ((Reg[0] & 0x0F00) == 0x0500)
271 {
272 /* Get the Model Number */
273 switch (Reg[0] & 0x00F0)
274 {
275 /* Check if this is the Model 1 */
276 case 0x0010:
277
278 /* Check if this is Step 0 or 1. They don't support PGE */
279 if ((Reg[0] & 0x000F) > 0x03) break;
280
281 case 0x0000:
282
283 /* Model 0 doesn't support PGE at all. */
284 Reg[3] &= ~0x2000;
285 break;
286
287 case 0x0080:
288
289 /* K6-2, Step 8 and over have support for MTRR. */
290 if ((Reg[0] & 0x000F) >= 0x8) FeatureBits |= KF_AMDK6MTRR;
291 break;
292
293 case 0x0090:
294
295 /* As does the K6-3 */
296 FeatureBits |= KF_AMDK6MTRR;
297 break;
298
299 default:
300 break;
301 }
302 }
303 }
304 else
305 {
306 /* Familes below 5 don't support PGE, PSE or CMOV at all */
307 Reg[3] &= ~(0x08 | 0x2000 | 0x8000);
308
309 /* They also don't support advanced CPUID functions. */
310 ExtendedCPUID = FALSE;
311 }
312
313 /* Set the current features */
314 CpuFeatures = Reg[3];
315 }
316
317 /* Now check if this is Intel */
318 if (Vendor == CPU_INTEL)
319 {
320 /* Check if it's a P6 */
321 if (Prcb->CpuType == 6)
322 {
323 /* Perform the special sequence to get the MicroCode Signature */
324 WRMSR(0x8B, 0);
325 CPUID(Reg, 1);
326 Prcb->UpdateSignature.QuadPart = RDMSR(0x8B);
327 }
328 else if (Prcb->CpuType == 5)
329 {
330 /* On P5, enable workaround for the LOCK errata. */
331 KiI386PentiumLockErrataPresent = TRUE;
332 }
333
334 /* Check for broken P6 with bad SMP PTE implementation */
335 if (((Reg[0] & 0x0FF0) == 0x0610 && (Reg[0] & 0x000F) <= 0x9) ||
336 ((Reg[0] & 0x0FF0) == 0x0630 && (Reg[0] & 0x000F) <= 0x4))
337 {
338 /* Remove support for correct PTE support. */
339 FeatureBits &= ~KF_WORKING_PTE;
340 }
341
342 /* Set the current features */
343 CpuFeatures = Reg[3];
344 }
345
346 /* Convert all CPUID Feature bits into our format */
347 if (CpuFeatures & 0x00000002) FeatureBits |= KF_V86_VIS | KF_CR4;
348 if (CpuFeatures & 0x00000008) FeatureBits |= KF_LARGE_PAGE | KF_CR4;
349 if (CpuFeatures & 0x00000010) FeatureBits |= KF_RDTSC;
350 if (CpuFeatures & 0x00000100) FeatureBits |= KF_CMPXCHG8B;
351 if (CpuFeatures & 0x00000800) FeatureBits |= KF_FAST_SYSCALL;
352 if (CpuFeatures & 0x00001000) FeatureBits |= KF_MTRR;
353 if (CpuFeatures & 0x00002000) FeatureBits |= KF_GLOBAL_PAGE | KF_CR4;
354 if (CpuFeatures & 0x00008000) FeatureBits |= KF_CMOV;
355 if (CpuFeatures & 0x00010000) FeatureBits |= KF_PAT;
356 if (CpuFeatures & 0x00800000) FeatureBits |= KF_MMX;
357 if (CpuFeatures & 0x01000000) FeatureBits |= KF_FXSR;
358 if (CpuFeatures & 0x02000000) FeatureBits |= KF_XMMI;
359 if (CpuFeatures & 0x04000000) FeatureBits |= KF_XMMI64;
360
361 /* Check if the CPU has hyper-threading */
362 if (CpuFeatures & 0x10000000)
363 {
364 /* Set the number of logical CPUs */
365 Prcb->LogicalProcessorsPerPhysicalProcessor = (UCHAR)(Reg[1] >> 16);
366 if (Prcb->LogicalProcessorsPerPhysicalProcessor > 1)
367 {
368 /* We're on dual-core */
369 KiSMTProcessorsPresent = TRUE;
370 }
371 }
372 else
373 {
374 /* We only have a single CPU */
375 Prcb->LogicalProcessorsPerPhysicalProcessor = 1;
376 }
377
378 /* Check if CPUID 0x80000000 is supported */
379 if (ExtendedCPUID)
380 {
381 /* Do the call */
382 CPUID(Reg, 0x80000000);
383 if ((Reg[0] & 0xffffff00) == 0x80000000)
384 {
385 /* Check if CPUID 0x80000001 is supported */
386 if (Reg[0] >= 0x80000001)
387 {
388 /* Check which extended features are available. */
389 CPUID(Reg, 0x80000001);
390
391 /* Now handle each features for each CPU Vendor */
392 switch (Vendor)
393 {
394 case CPU_AMD:
395 if (Reg[3] & 0x80000000) FeatureBits |= KF_3DNOW;
396 break;
397 }
398 }
399 }
400 }
401
402 /* Return the Feature Bits */
403 return FeatureBits;
404 }
405
406 VOID
407 NTAPI
408 KiGetCacheInformation(VOID)
409 {
410 PKIPCR Pcr = (PKIPCR)KeGetPcr();
411 ULONG Vendor;
412 ULONG Data[4];
413 ULONG CacheRequests = 0, i;
414 ULONG CurrentRegister;
415 UCHAR RegisterByte;
416 BOOLEAN FirstPass = TRUE;
417
418 /* Set default L2 size */
419 Pcr->SecondLevelCacheSize = 0;
420
421 /* Get the Vendor ID and make sure we support CPUID */
422 Vendor = KiGetCpuVendor();
423 if (!Vendor) return;
424
425 /* Check the Vendor ID */
426 switch (Vendor)
427 {
428 /* Handle Intel case */
429 case CPU_INTEL:
430
431 /*Check if we support CPUID 2 */
432 CPUID(Data, 0);
433 if (Data[0] >= 2)
434 {
435 /* We need to loop for the number of times CPUID will tell us to */
436 do
437 {
438 /* Do the CPUID call */
439 CPUID(Data, 2);
440
441 /* Check if it was the first call */
442 if (FirstPass)
443 {
444 /*
445 * The number of times to loop is the first byte. Read
446 * it and then destroy it so we don't get confused.
447 */
448 CacheRequests = Data[0] & 0xFF;
449 Data[0] &= 0xFFFFFF00;
450
451 /* Don't go over this again */
452 FirstPass = FALSE;
453 }
454
455 /* Loop all 4 registers */
456 for (i = 0; i < 4; i++)
457 {
458 /* Get the current register */
459 CurrentRegister = Data[i];
460
461 /*
462 * If the upper bit is set, then this register should
463 * be skipped.
464 */
465 if (CurrentRegister & 0x80000000) continue;
466
467 /* Keep looping for every byte inside this register */
468 while (CurrentRegister)
469 {
470 /* Read a byte, skip a byte. */
471 RegisterByte = (UCHAR)(CurrentRegister & 0xFF);
472 CurrentRegister >>= 8;
473 if (!RegisterByte) continue;
474
475 /*
476 * Valid values are from 0x40 (0 bytes) to 0x49
477 * (32MB), or from 0x80 to 0x89 (same size but
478 * 8-way associative.
479 */
480 if (((RegisterByte > 0x40) &&
481 (RegisterByte <= 0x49)) ||
482 ((RegisterByte > 0x80) &&
483 (RegisterByte <= 0x89)))
484 {
485 /* Mask out only the first nibble */
486 RegisterByte &= 0x0F;
487
488 /* Set the L2 Cache Size */
489 Pcr->SecondLevelCacheSize = 0x10000 <<
490 RegisterByte;
491 }
492 }
493 }
494 } while (--CacheRequests);
495 }
496 break;
497
498 case CPU_AMD:
499
500 /* FIXME */
501 DPRINT1("Not handling AMD caches yet\n");
502 break;
503 }
504 }
505
506 VOID
507 NTAPI
508 KiSetCR0Bits(VOID)
509 {
510 ULONG Cr0;
511
512 /* Save current CR0 */
513 Cr0 = Ke386GetCr0();
514
515 /* If this is a 486, enable Write-Protection */
516 if (KeGetCurrentPrcb()->CpuType > 3) Cr0 |= CR0_WP;
517
518 /* Set new Cr0 */
519 Ke386SetCr0(Cr0);
520 }
521
522 VOID
523 NTAPI
524 KiInitializeTSS2(IN PKTSS Tss,
525 IN PKGDTENTRY TssEntry OPTIONAL)
526 {
527 PUCHAR p;
528
529 /* Make sure the GDT Entry is valid */
530 if (TssEntry)
531 {
532 /* Set the Limit */
533 TssEntry->LimitLow = sizeof(KTSS) - 1;
534 TssEntry->HighWord.Bits.LimitHi &= 0xF0;
535 }
536
537 /* Now clear the I/O Map */
538 RtlFillMemory(Tss->IoMaps[0].IoMap, 8096, -1);
539
540 /* Initialize Interrupt Direction Maps */
541 p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);
542 RtlZeroMemory(p, 32);
543
544 /* Add DPMI support for interrupts */
545 p[0] = 4;
546 p[3] = 0x18;
547 p[4] = 0x18;
548
549 /* Initialize the default Interrupt Direction Map */
550 p = Tss->IntDirectionMap;
551 RtlZeroMemory(Tss->IntDirectionMap, 32);
552
553 /* Add DPMI support */
554 p[0] = 4;
555 p[3] = 0x18;
556 p[4] = 0x18;
557 }
558
559 VOID
560 NTAPI
561 KiInitializeTSS(IN PKTSS Tss)
562 {
563 /* Set an invalid map base */
564 Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
565
566 /* Disable traps during Task Switches */
567 Tss->Flags = 0;
568
569 /* Set LDT and Ring 0 SS */
570 Tss->LDT = 0;
571 Tss->Ss0 = KGDT_R0_DATA;
572 }
573
574 VOID
575 FASTCALL
576 Ki386InitializeTss(IN PKTSS Tss,
577 IN PKIDTENTRY Idt,
578 IN PKGDTENTRY Gdt)
579 {
580 PKGDTENTRY TssEntry, TaskGateEntry;
581
582 /* Initialize the boot TSS. */
583 TssEntry = &Gdt[KGDT_TSS / sizeof(KGDTENTRY)];
584 TssEntry->HighWord.Bits.Type = I386_TSS;
585 TssEntry->HighWord.Bits.Pres = 1;
586 TssEntry->HighWord.Bits.Dpl = 0;
587 KiInitializeTSS2(Tss, TssEntry);
588 KiInitializeTSS(Tss);
589
590 /* Load the task register */
591 Ke386SetTr(KGDT_TSS);
592
593 /* Setup the Task Gate for Double Fault Traps */
594 TaskGateEntry = (PKGDTENTRY)&Idt[8];
595 TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
596 TaskGateEntry->HighWord.Bits.Pres = 1;
597 TaskGateEntry->HighWord.Bits.Dpl = 0;
598 ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_DF_TSS;
599
600 /* Initialize the TSS used for handling double faults. */
601 Tss = (PKTSS)KiDoubleFaultTSS;
602 KiInitializeTSS(Tss);
603 Tss->CR3 = _Ke386GetCr(3);
604 Tss->Esp0 = PtrToUlong(KiDoubleFaultStack);
605 Tss->Eip = PtrToUlong(KiTrap8);
606 Tss->Cs = KGDT_R0_CODE;
607 Tss->Fs = KGDT_R0_PCR;
608 Tss->Ss = Ke386GetSs();
609 Tss->Es = KGDT_R3_DATA | RPL_MASK;
610 Tss->Ds = KGDT_R3_DATA | RPL_MASK;
611
612 /* Setup the Double Trap TSS entry in the GDT */
613 TssEntry = &Gdt[KGDT_DF_TSS / sizeof(KGDTENTRY)];
614 TssEntry->HighWord.Bits.Type = I386_TSS;
615 TssEntry->HighWord.Bits.Pres = 1;
616 TssEntry->HighWord.Bits.Dpl = 0;
617 TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
618 TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
619 TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
620 TssEntry->LimitLow = KTSS_IO_MAPS;
621
622 /* Now setup the NMI Task Gate */
623 TaskGateEntry = (PKGDTENTRY)&Idt[2];
624 TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
625 TaskGateEntry->HighWord.Bits.Pres = 1;
626 TaskGateEntry->HighWord.Bits.Dpl = 0;
627 ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_NMI_TSS;
628
629 /* Initialize the actual TSS */
630 Tss = (PKTSS)KiNMITSS;
631 KiInitializeTSS(Tss);
632 Tss->CR3 = _Ke386GetCr(3);
633 Tss->Esp0 = PtrToUlong(KiDoubleFaultStack);
634 Tss->Eip = PtrToUlong(KiTrap2);
635 Tss->Cs = KGDT_R0_CODE;
636 Tss->Fs = KGDT_R0_PCR;
637 Tss->Ss = Ke386GetSs();
638 Tss->Es = KGDT_R3_DATA | RPL_MASK;
639 Tss->Ds = KGDT_R3_DATA | RPL_MASK;
640
641 /* And its associated TSS Entry */
642 TssEntry = &Gdt[KGDT_NMI_TSS / sizeof(KGDTENTRY)];
643 TssEntry->HighWord.Bits.Type = I386_TSS;
644 TssEntry->HighWord.Bits.Pres = 1;
645 TssEntry->HighWord.Bits.Dpl = 0;
646 TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
647 TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
648 TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
649 TssEntry->LimitLow = KTSS_IO_MAPS;
650 }
651
652 VOID
653 INIT_FUNCTION
654 Ki386SetProcessorFeatures(VOID)
655 {
656 OBJECT_ATTRIBUTES ObjectAttributes;
657 UNICODE_STRING KeyName =
658 RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Kernel");
659 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"FastSystemCallDisable");
660 HANDLE KeyHandle;
661 ULONG ResultLength;
662 struct
663 {
664 KEY_VALUE_PARTIAL_INFORMATION Info;
665 UCHAR Buffer[20];
666 } ValueData;
667 NTSTATUS Status;
668 ULONG FastSystemCallDisable = 0;
669
670 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
671 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = FALSE;
672 SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
673 (KeFeatureBits & KF_CMPXCHG8B) ? TRUE : FALSE;
674 SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
675 (KeFeatureBits & KF_MMX) ? TRUE : FALSE;
676 SharedUserData->ProcessorFeatures[PF_PPC_MOVEMEM_64BIT_OK] = FALSE;
677 SharedUserData->ProcessorFeatures[PF_ALPHA_BYTE_INSTRUCTIONS] = FALSE;
678 SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
679 (KeFeatureBits & KF_XMMI) ? TRUE : FALSE;
680 SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
681 (KeFeatureBits & KF_RDTSC) ? TRUE : FALSE;
682
683 /* Does the CPU Support Fast System Call? */
684 if (KeFeatureBits & KF_FAST_SYSCALL) {
685
686 /* FIXME: Check for Family == 6, Model < 3 and Stepping < 3 and disable */
687
688 /* Make sure it's not disabled in registry */
689 InitializeObjectAttributes(&ObjectAttributes,
690 &KeyName,
691 OBJ_CASE_INSENSITIVE,
692 NULL,
693 NULL);
694 Status = ZwOpenKey(&KeyHandle,
695 KEY_QUERY_VALUE,
696 &ObjectAttributes);
697
698 if (NT_SUCCESS(Status)) {
699
700 /* Read the Value then Close the Key */
701 Status = ZwQueryValueKey(KeyHandle,
702 &ValueName,
703 KeyValuePartialInformation,
704 &ValueData,
705 sizeof(ValueData),
706 &ResultLength);
707 if (NT_SUCCESS(Status))
708 {
709 if (ResultLength == sizeof(ValueData) &&
710 ValueData.Info.Type == REG_DWORD)
711 {
712 FastSystemCallDisable = *(PULONG)ValueData.Info.Data != 0;
713 }
714
715 ZwClose(KeyHandle);
716 }
717 }
718
719 } else {
720
721 /* Disable SYSENTER/SYSEXIT, because the CPU doesn't support it */
722 FastSystemCallDisable = 1;
723
724 }
725
726 if (FastSystemCallDisable) {
727 /* Use INT2E */
728 const unsigned char Entry[7] = {0x8D, 0x54, 0x24, 0x08, /* lea 0x8(%esp),%edx */
729 0xCD, 0x2E, /* int 0x2e */
730 0xC3}; /* ret */
731 memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));
732 } else {
733 /* Use SYSENTER */
734 const unsigned char Entry[5] = {0x8B, 0xD4, /* movl %esp,%edx */
735 0x0F, 0x34, /* sysenter */
736 0xC3}; /* ret */
737 memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));
738 /* Enable SYSENTER/SYSEXIT */
739 KiFastSystemCallDisable = 0;
740 }
741 }
742
743 VOID
744 NTAPI
745 KeFlushCurrentTb(VOID)
746 {
747 /* Flush the TLB by resetting CR3 */
748 _Ke386SetCr(3, _Ke386GetCr(3));
749 }
750
751 VOID
752 NTAPI
753 KiSaveProcessorControlState(IN PKPROCESSOR_STATE ProcessorState)
754 {
755 /* Save the CR registers */
756 ProcessorState->SpecialRegisters.Cr0 = _Ke386GetCr(0);
757 ProcessorState->SpecialRegisters.Cr2 = _Ke386GetCr(2);
758 ProcessorState->SpecialRegisters.Cr3 = _Ke386GetCr(3);
759 ProcessorState->SpecialRegisters.Cr4 = _Ke386GetCr(4);
760
761 /* Save the DR registers */
762 ProcessorState->SpecialRegisters.KernelDr0 = _Ke386GetDr(0);
763 ProcessorState->SpecialRegisters.KernelDr1 = _Ke386GetDr(1);
764 ProcessorState->SpecialRegisters.KernelDr2 = _Ke386GetDr(2);
765 ProcessorState->SpecialRegisters.KernelDr3 = _Ke386GetDr(3);
766 ProcessorState->SpecialRegisters.KernelDr6 = _Ke386GetDr(6);
767 ProcessorState->SpecialRegisters.KernelDr7 = _Ke386GetDr(7);
768 _Ke386SetDr(7, 0);
769
770 /* Save GDT, IDT, LDT and TSS */
771 Ke386GetGlobalDescriptorTable(ProcessorState->SpecialRegisters.Gdtr);
772 Ke386GetInterruptDescriptorTable(ProcessorState->SpecialRegisters.Idtr);
773 Ke386GetTr(ProcessorState->SpecialRegisters.Tr);
774 Ke386GetLocalDescriptorTable(ProcessorState->SpecialRegisters.Ldtr);
775 }
776
777 VOID
778 NTAPI
779 KiInitializeMachineType(VOID)
780 {
781 /* Set the Machine Type we got from NTLDR */
782 KeI386MachineType = KeLoaderBlock->u.I386.MachineType & 0x000FF;
783 }
784
785 ULONG_PTR
786 NTAPI
787 KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
788 {
789 /* Set CS and ESP */
790 Ke386Wrmsr(0x174, KGDT_R0_CODE, 0);
791 Ke386Wrmsr(0x175, KeGetCurrentPrcb()->DpcStack, 0);
792
793 /* Set LSTAR */
794 Ke386Wrmsr(0x176, KiFastCallEntry, 0);
795 return 0;
796 }
797
798 VOID
799 NTAPI
800 KiRestoreFastSyscallReturnState(VOID)
801 {
802 /* FIXME: NT has support for SYSCALL, IA64-SYSENTER, etc. */
803
804 /* Check if the CPU Supports fast system call */
805 if (KeFeatureBits & KF_FAST_SYSCALL)
806 {
807 /* Do an IPI to enable it */
808 KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters, 0);
809 }
810 }
811
812 ULONG_PTR
813 NTAPI
814 Ki386EnableDE(IN ULONG_PTR Context)
815 {
816 /* Enable DE */
817 Ke386SetCr4(Ke386GetCr4() | CR4_DE);
818 return 0;
819 }
820
821 ULONG_PTR
822 NTAPI
823 Ki386EnableFxsr(IN ULONG_PTR Context)
824 {
825 /* Enable FXSR */
826 Ke386SetCr4(Ke386GetCr4() | CR4_FXSR);
827 return 0;
828 }
829
830 ULONG_PTR
831 NTAPI
832 Ki386EnableXMMIExceptions(IN ULONG_PTR Context)
833 {
834 /* FIXME: Support this */
835 DPRINT1("Your machine supports XMMI exceptions but ReactOS doesn't\n");
836 return 0;
837 }
838
839 VOID
840 NTAPI
841 KiI386PentiumLockErrataFixup(VOID)
842 {
843 /* FIXME: Support this */
844 DPRINT1("WARNING: Your machine has a CPU bug that ReactOS can't bypass!\n");
845 }
846
847 /* PUBLIC FUNCTIONS **********************************************************/
848
849 /*
850 * @implemented
851 */
852 NTSTATUS
853 NTAPI
854 KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)
855 {
856 PFNSAVE_FORMAT FpState;
857 ASSERT_IRQL(DISPATCH_LEVEL);
858 DPRINT1("%s is not really implemented\n", __FUNCTION__);
859
860 /* check if we are doing software emulation */
861 if (!KeI386NpxPresent) return STATUS_ILLEGAL_FLOAT_CONTEXT;
862
863 FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT));
864 if (!FpState) return STATUS_INSUFFICIENT_RESOURCES;
865
866 *((PVOID *) Save) = FpState;
867 #ifdef __GNUC__
868 asm volatile("fnsave %0\n\t" : "=m" (*FpState));
869 #else
870 __asm
871 {
872 fnsave [FpState]
873 };
874 #endif
875
876 KeGetCurrentThread()->DispatcherHeader.NpxIrql = KeGetCurrentIrql();
877 return STATUS_SUCCESS;
878 }
879
880 /*
881 * @implemented
882 */
883 NTSTATUS
884 NTAPI
885 KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save)
886 {
887 PFNSAVE_FORMAT FpState = *((PVOID *) Save);
888 ASSERT(KeGetCurrentThread()->DispatcherHeader.NpxIrql == KeGetCurrentIrql());
889 DPRINT1("%s is not really implemented\n", __FUNCTION__);
890
891 #ifdef __GNUC__
892 asm volatile("fnclex\n\t");
893 asm volatile("frstor %0\n\t" : "=m" (*FpState));
894 #else
895 __asm
896 {
897 fnclex
898 frstor [FpState]
899 };
900 #endif
901
902 ExFreePool(FpState);
903 return STATUS_SUCCESS;
904 }
905
906 /*
907 * @implemented
908 */
909 ULONG
910 NTAPI
911 KeGetRecommendedSharedDataAlignment(VOID)
912 {
913 /* Return the global variable */
914 return KeLargestCacheLine;
915 }
916
917 /*
918 * @implemented
919 */
920 VOID
921 NTAPI
922 KeFlushEntireTb(IN BOOLEAN Invalid,
923 IN BOOLEAN AllProcessors)
924 {
925 KIRQL OldIrql;
926
927 /* Raise the IRQL for the TB Flush */
928 OldIrql = KeRaiseIrqlToSynchLevel();
929
930 #ifdef CONFIG_SMP
931 /* FIXME: Support IPI Flush */
932 #error Not yet implemented!
933 #endif
934
935 /* Flush the TB for the Current CPU */
936 KeFlushCurrentTb();
937
938 /* Return to Original IRQL */
939 KeLowerIrql(OldIrql);
940 }
941
942 /*
943 * @implemented
944 */
945 VOID
946 NTAPI
947 KeSetDmaIoCoherency(IN ULONG Coherency)
948 {
949 /* Save the coherency globally */
950 KiDmaIoCoherency = Coherency;
951 }
952
953 /*
954 * @implemented
955 */
956 KAFFINITY
957 NTAPI
958 KeQueryActiveProcessors(VOID)
959 {
960 PAGED_CODE();
961
962 /* Simply return the number of active processors */
963 return KeActiveProcessors;
964 }
965
966 /*
967 * @implemented
968 */
969 VOID
970 __cdecl
971 KeSaveStateForHibernate(IN PKPROCESSOR_STATE State)
972 {
973 /* Capture the context */
974 RtlCaptureContext(&State->ContextFrame);
975
976 /* Capture the control state */
977 KiSaveProcessorControlState(State);
978 }