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