[NTOSKRNL]
[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 /* Check if the CPU is too old to support SYSENTER */
308 if ((Prcb->CpuType < 6) ||
309 ((Prcb->CpuType == 6) && (Prcb->CpuStep < 0x0303)))
310 {
311 /* Disable it */
312 Reg[3] &= ~0x800;
313 }
314
315 /* Set the current features */
316 CpuFeatures = Reg[3];
317
318 break;
319
320 /* AMD CPUs */
321 case CPU_AMD:
322
323 /* Check if this is a K5 or K6. (family 5) */
324 if ((Reg[0] & 0x0F00) == 0x0500)
325 {
326 /* Get the Model Number */
327 switch (Reg[0] & 0x00F0)
328 {
329 /* Model 1: K5 - 5k86 (initial models) */
330 case 0x0010:
331
332 /* Check if this is Step 0 or 1. They don't support PGE */
333 if ((Reg[0] & 0x000F) > 0x03) break;
334
335 /* Model 0: K5 - SSA5 */
336 case 0x0000:
337
338 /* Model 0 doesn't support PGE at all. */
339 Reg[3] &= ~0x2000;
340 break;
341
342 /* Model 8: K6-2 */
343 case 0x0080:
344
345 /* K6-2, Step 8 and over have support for MTRR. */
346 if ((Reg[0] & 0x000F) >= 0x8) FeatureBits |= KF_AMDK6MTRR;
347 break;
348
349 /* Model 9: K6-III
350 Model D: K6-2+, K6-III+ */
351 case 0x0090:
352 case 0x00D0:
353
354 FeatureBits |= KF_AMDK6MTRR;
355 break;
356 }
357 }
358 else if((Reg[0] & 0x0F00) < 0x0500)
359 {
360 /* Families below 5 don't support PGE, PSE or CMOV at all */
361 Reg[3] &= ~(0x08 | 0x2000 | 0x8000);
362
363 /* They also don't support advanced CPUID functions. */
364 ExtendedCPUID = FALSE;
365 }
366
367 /* Set the current features */
368 CpuFeatures = Reg[3];
369
370 break;
371
372 /* Cyrix CPUs */
373 case CPU_CYRIX:
374
375 /* Workaround the "COMA" bug on 6x family of Cyrix CPUs */
376 if (Prcb->CpuType == 6 &&
377 Prcb->CpuStep <= 1)
378 {
379 /* Get CCR1 value */
380 Ccr1 = getCx86(CX86_CCR1);
381
382 /* Enable the NO_LOCK bit */
383 Ccr1 |= 0x10;
384
385 /* Set the new CCR1 value */
386 setCx86(CX86_CCR1, Ccr1);
387 }
388
389 /* Set the current features */
390 CpuFeatures = Reg[3];
391
392 break;
393
394 /* Transmeta CPUs */
395 case CPU_TRANSMETA:
396
397 /* Enable CMPXCHG8B if the family (>= 5), model and stepping (>= 4.2) support it */
398 if ((Reg[0] & 0x0FFF) >= 0x0542)
399 {
400 WRMSR(0x80860004, RDMSR(0x80860004) | 0x0100);
401 FeatureBits |= KF_CMPXCHG8B;
402 }
403
404 break;
405
406 /* Centaur, IDT, Rise and VIA CPUs */
407 case CPU_CENTAUR:
408 case CPU_RISE:
409
410 /* These CPUs don't report the presence of CMPXCHG8B through CPUID.
411 However, this feature exists and operates properly without any additional steps. */
412 FeatureBits |= KF_CMPXCHG8B;
413
414 break;
415 }
416
417 /* Convert all CPUID Feature bits into our format */
418 if (CpuFeatures & 0x00000002) FeatureBits |= KF_V86_VIS | KF_CR4;
419 if (CpuFeatures & 0x00000008) FeatureBits |= KF_LARGE_PAGE | KF_CR4;
420 if (CpuFeatures & 0x00000010) FeatureBits |= KF_RDTSC;
421 if (CpuFeatures & 0x00000100) FeatureBits |= KF_CMPXCHG8B;
422 if (CpuFeatures & 0x00000800) FeatureBits |= KF_FAST_SYSCALL;
423 if (CpuFeatures & 0x00001000) FeatureBits |= KF_MTRR;
424 if (CpuFeatures & 0x00002000) FeatureBits |= KF_GLOBAL_PAGE | KF_CR4;
425 if (CpuFeatures & 0x00008000) FeatureBits |= KF_CMOV;
426 if (CpuFeatures & 0x00010000) FeatureBits |= KF_PAT;
427 if (CpuFeatures & 0x00200000) FeatureBits |= KF_DTS;
428 if (CpuFeatures & 0x00800000) FeatureBits |= KF_MMX;
429 if (CpuFeatures & 0x01000000) FeatureBits |= KF_FXSR;
430 if (CpuFeatures & 0x02000000) FeatureBits |= KF_XMMI;
431 if (CpuFeatures & 0x04000000) FeatureBits |= KF_XMMI64;
432
433 /* Check if the CPU has hyper-threading */
434 if (CpuFeatures & 0x10000000)
435 {
436 /* Set the number of logical CPUs */
437 Prcb->LogicalProcessorsPerPhysicalProcessor = (UCHAR)(Reg[1] >> 16);
438 if (Prcb->LogicalProcessorsPerPhysicalProcessor > 1)
439 {
440 /* We're on dual-core */
441 KiSMTProcessorsPresent = TRUE;
442 }
443 }
444 else
445 {
446 /* We only have a single CPU */
447 Prcb->LogicalProcessorsPerPhysicalProcessor = 1;
448 }
449
450 /* Check if CPUID 0x80000000 is supported */
451 if (ExtendedCPUID)
452 {
453 /* Do the call */
454 CPUID(0x80000000, &Reg[0], &Dummy, &Dummy, &Dummy);
455 if ((Reg[0] & 0xffffff00) == 0x80000000)
456 {
457 /* Check if CPUID 0x80000001 is supported */
458 if (Reg[0] >= 0x80000001)
459 {
460 /* Check which extended features are available. */
461 CPUID(0x80000001, &Dummy, &Dummy, &Dummy, &Reg[3]);
462
463 /* Check if NX-bit is supported */
464 if (Reg[3] & 0x00100000) FeatureBits |= KF_NX_BIT;
465
466 /* Now handle each features for each CPU Vendor */
467 switch (Vendor)
468 {
469 case CPU_AMD:
470 case CPU_CENTAUR:
471 if (Reg[3] & 0x80000000) FeatureBits |= KF_3DNOW;
472 break;
473 }
474 }
475 }
476 }
477
478 #define print_supported(kf_value) ((FeatureBits & kf_value) ? #kf_value : "")
479 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",
480 print_supported(KF_V86_VIS),
481 print_supported(KF_RDTSC),
482 print_supported(KF_CR4),
483 print_supported(KF_CMOV),
484 print_supported(KF_GLOBAL_PAGE),
485 print_supported(KF_LARGE_PAGE),
486 print_supported(KF_MTRR),
487 print_supported(KF_CMPXCHG8B),
488 print_supported(KF_MMX),
489 print_supported(KF_WORKING_PTE),
490 print_supported(KF_PAT),
491 print_supported(KF_FXSR),
492 print_supported(KF_FAST_SYSCALL),
493 print_supported(KF_XMMI),
494 print_supported(KF_3DNOW),
495 print_supported(KF_AMDK6MTRR),
496 print_supported(KF_XMMI64),
497 print_supported(KF_DTS),
498 print_supported(KF_NX_BIT),
499 print_supported(KF_NX_DISABLED),
500 print_supported(KF_NX_ENABLED));
501 #undef print_supported
502
503 /* Return the Feature Bits */
504 return FeatureBits;
505 }
506
507 VOID
508 NTAPI
509 INIT_FUNCTION
510 KiGetCacheInformation(VOID)
511 {
512 PKIPCR Pcr = (PKIPCR)KeGetPcr();
513 ULONG Vendor;
514 ULONG Data[4], Dummy;
515 ULONG CacheRequests = 0, i;
516 ULONG CurrentRegister;
517 UCHAR RegisterByte;
518 ULONG Size, Associativity = 0, CacheLine = 64, CurrentSize = 0;
519 BOOLEAN FirstPass = TRUE;
520
521 /* Set default L2 size */
522 Pcr->SecondLevelCacheSize = 0;
523
524 /* Get the Vendor ID and make sure we support CPUID */
525 Vendor = KiGetCpuVendor();
526 if (!Vendor) return;
527
528 /* Check the Vendor ID */
529 switch (Vendor)
530 {
531 /* Handle Intel case */
532 case CPU_INTEL:
533
534 /*Check if we support CPUID 2 */
535 CPUID(0, &Data[0], &Dummy, &Dummy, &Dummy);
536 if (Data[0] >= 2)
537 {
538 /* We need to loop for the number of times CPUID will tell us to */
539 do
540 {
541 /* Do the CPUID call */
542 CPUID(2, &Data[0], &Data[1], &Data[2], &Data[3]);
543
544 /* Check if it was the first call */
545 if (FirstPass)
546 {
547 /*
548 * The number of times to loop is the first byte. Read
549 * it and then destroy it so we don't get confused.
550 */
551 CacheRequests = Data[0] & 0xFF;
552 Data[0] &= 0xFFFFFF00;
553
554 /* Don't go over this again */
555 FirstPass = FALSE;
556 }
557
558 /* Loop all 4 registers */
559 for (i = 0; i < 4; i++)
560 {
561 /* Get the current register */
562 CurrentRegister = Data[i];
563
564 /*
565 * If the upper bit is set, then this register should
566 * be skipped.
567 */
568 if (CurrentRegister & 0x80000000) continue;
569
570 /* Keep looping for every byte inside this register */
571 while (CurrentRegister)
572 {
573 /* Read a byte, skip a byte. */
574 RegisterByte = (UCHAR)(CurrentRegister & 0xFF);
575 CurrentRegister >>= 8;
576 if (!RegisterByte) continue;
577
578 /*
579 * Valid values are from 0x40 (0 bytes) to 0x49
580 * (32MB), or from 0x80 to 0x89 (same size but
581 * 8-way associative.
582 */
583 if (((RegisterByte > 0x40) && (RegisterByte <= 0x47)) ||
584 ((RegisterByte > 0x78) && (RegisterByte <= 0x7C)) ||
585 ((RegisterByte > 0x80) && (RegisterByte <= 0x85)))
586 {
587 /* Compute associativity */
588 Associativity = 4;
589 if (RegisterByte >= 0x79) Associativity = 8;
590
591 /* Mask out only the first nibble */
592 RegisterByte &= 0x07;
593
594 /* Check if this cache is bigger than the last */
595 Size = 0x10000 << RegisterByte;
596 if ((Size / Associativity) > CurrentSize)
597 {
598 /* Set the L2 Cache Size and Associativity */
599 CurrentSize = Size / Associativity;
600 Pcr->SecondLevelCacheSize = Size;
601 Pcr->SecondLevelCacheAssociativity = Associativity;
602 }
603 }
604 else if ((RegisterByte > 0x21) && (RegisterByte <= 0x29))
605 {
606 /* Set minimum cache line size */
607 if (CacheLine < 128) CacheLine = 128;
608
609 /* Hard-code size/associativity */
610 Associativity = 8;
611 switch (RegisterByte)
612 {
613 case 0x22:
614 Size = 512 * 1024;
615 Associativity = 4;
616 break;
617
618 case 0x23:
619 Size = 1024 * 1024;
620 break;
621
622 case 0x25:
623 Size = 2048 * 1024;
624 break;
625
626 case 0x29:
627 Size = 4096 * 1024;
628 break;
629
630 default:
631 Size = 0;
632 break;
633 }
634
635 /* Check if this cache is bigger than the last */
636 if ((Size / Associativity) > CurrentSize)
637 {
638 /* Set the L2 Cache Size and Associativity */
639 CurrentSize = Size / Associativity;
640 Pcr->SecondLevelCacheSize = Size;
641 Pcr->SecondLevelCacheAssociativity = Associativity;
642 }
643 }
644 else if (((RegisterByte > 0x65) && (RegisterByte < 0x69)) ||
645 (RegisterByte == 0x2C) || (RegisterByte == 0xF0))
646 {
647 /* Indicates L1 cache line of 64 bytes */
648 KePrefetchNTAGranularity = 64;
649 }
650 else if (RegisterByte == 0xF1)
651 {
652 /* Indicates L1 cache line of 128 bytes */
653 KePrefetchNTAGranularity = 128;
654 }
655 else if (((RegisterByte >= 0x4A) && (RegisterByte <= 0x4C)) ||
656 (RegisterByte == 0x78) ||
657 (RegisterByte == 0x7D) ||
658 (RegisterByte == 0x7F) ||
659 (RegisterByte == 0x86) ||
660 (RegisterByte == 0x87))
661 {
662 /* Set minimum cache line size */
663 if (CacheLine < 64) CacheLine = 64;
664
665 /* Hard-code size/associativity */
666 switch (RegisterByte)
667 {
668 case 0x4A:
669 Size = 4 * 1024 * 1024;
670 Associativity = 8;
671 break;
672
673 case 0x4B:
674 Size = 6 * 1024 * 1024;
675 Associativity = 12;
676 break;
677
678 case 0x4C:
679 Size = 8 * 1024 * 1024;
680 Associativity = 16;
681 break;
682
683 case 0x78:
684 Size = 1 * 1024 * 1024;
685 Associativity = 4;
686 break;
687
688 case 0x7D:
689 Size = 2 * 1024 * 1024;
690 Associativity = 8;
691 break;
692
693 case 0x7F:
694 Size = 512 * 1024;
695 Associativity = 2;
696 break;
697
698 case 0x86:
699 Size = 512 * 1024;
700 Associativity = 4;
701 break;
702
703 case 0x87:
704 Size = 1 * 1024 * 1024;
705 Associativity = 8;
706 break;
707
708 default:
709 Size = 0;
710 break;
711 }
712
713 /* Check if this cache is bigger than the last */
714 if ((Size / Associativity) > CurrentSize)
715 {
716 /* Set the L2 Cache Size and Associativity */
717 CurrentSize = Size / Associativity;
718 Pcr->SecondLevelCacheSize = Size;
719 Pcr->SecondLevelCacheAssociativity = Associativity;
720 }
721 }
722 }
723 }
724 } while (--CacheRequests);
725 }
726 break;
727
728 case CPU_AMD:
729
730 /* Check if we support CPUID 0x80000005 */
731 CPUID(0x80000000, &Data[0], &Data[1], &Data[2], &Data[3]);
732 if (Data[0] >= 0x80000006)
733 {
734 /* Get L1 size first */
735 CPUID(0x80000005, &Data[0], &Data[1], &Data[2], &Data[3]);
736 KePrefetchNTAGranularity = Data[2] & 0xFF;
737
738 /* Check if we support CPUID 0x80000006 */
739 CPUID(0x80000000, &Data[0], &Data[1], &Data[2], &Data[3]);
740 if (Data[0] >= 0x80000006)
741 {
742 /* Get 2nd level cache and tlb size */
743 CPUID(0x80000006, &Data[0], &Data[1], &Data[2], &Data[3]);
744
745 /* Cache line size */
746 CacheLine = Data[2] & 0xFF;
747
748 /* Hardcode associativity */
749 RegisterByte = Data[2] >> 12;
750 switch (RegisterByte)
751 {
752 case 2:
753 Associativity = 2;
754 break;
755
756 case 4:
757 Associativity = 4;
758 break;
759
760 case 6:
761 Associativity = 8;
762 break;
763
764 case 8:
765 case 15:
766 Associativity = 16;
767 break;
768
769 default:
770 Associativity = 1;
771 break;
772 }
773
774 /* Compute size */
775 Size = (Data[2] >> 16) << 10;
776
777 /* Hack for Model 6, Steping 300 */
778 if ((KeGetCurrentPrcb()->CpuType == 6) &&
779 (KeGetCurrentPrcb()->CpuStep == 0x300))
780 {
781 /* Stick 64K in there */
782 Size = 64 * 1024;
783 }
784
785 /* Set the L2 Cache Size and associativity */
786 Pcr->SecondLevelCacheSize = Size;
787 Pcr->SecondLevelCacheAssociativity = Associativity;
788 }
789 }
790 break;
791
792 case CPU_CYRIX:
793 case CPU_TRANSMETA:
794 case CPU_CENTAUR:
795 case CPU_RISE:
796
797 /* FIXME */
798 break;
799 }
800
801 /* Set the cache line */
802 if (CacheLine > KeLargestCacheLine) KeLargestCacheLine = CacheLine;
803 DPRINT1("Prefetch Cache: %d bytes\tL2 Cache: %d bytes\tL2 Cache Line: %d bytes\tL2 Cache Associativity: %d\n",
804 KePrefetchNTAGranularity,
805 Pcr->SecondLevelCacheSize,
806 KeLargestCacheLine,
807 Pcr->SecondLevelCacheAssociativity);
808 }
809
810 VOID
811 NTAPI
812 INIT_FUNCTION
813 KiSetCR0Bits(VOID)
814 {
815 ULONG Cr0;
816
817 /* Save current CR0 */
818 Cr0 = __readcr0();
819
820 /* If this is a 486, enable Write-Protection */
821 if (KeGetCurrentPrcb()->CpuType > 3) Cr0 |= CR0_WP;
822
823 /* Set new Cr0 */
824 __writecr0(Cr0);
825 }
826
827 VOID
828 NTAPI
829 INIT_FUNCTION
830 KiInitializeTSS2(IN PKTSS Tss,
831 IN PKGDTENTRY TssEntry OPTIONAL)
832 {
833 PUCHAR p;
834
835 /* Make sure the GDT Entry is valid */
836 if (TssEntry)
837 {
838 /* Set the Limit */
839 TssEntry->LimitLow = sizeof(KTSS) - 1;
840 TssEntry->HighWord.Bits.LimitHi = 0;
841 }
842
843 /* Now clear the I/O Map */
844 ASSERT(IOPM_COUNT == 1);
845 RtlFillMemory(Tss->IoMaps[0].IoMap, IOPM_FULL_SIZE, 0xFF);
846
847 /* Initialize Interrupt Direction Maps */
848 p = (PUCHAR)(Tss->IoMaps[0].DirectionMap);
849 RtlZeroMemory(p, IOPM_DIRECTION_MAP_SIZE);
850
851 /* Add DPMI support for interrupts */
852 p[0] = 4;
853 p[3] = 0x18;
854 p[4] = 0x18;
855
856 /* Initialize the default Interrupt Direction Map */
857 p = Tss->IntDirectionMap;
858 RtlZeroMemory(Tss->IntDirectionMap, IOPM_DIRECTION_MAP_SIZE);
859
860 /* Add DPMI support */
861 p[0] = 4;
862 p[3] = 0x18;
863 p[4] = 0x18;
864 }
865
866 VOID
867 NTAPI
868 KiInitializeTSS(IN PKTSS Tss)
869 {
870 /* Set an invalid map base */
871 Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
872
873 /* Disable traps during Task Switches */
874 Tss->Flags = 0;
875
876 /* Set LDT and Ring 0 SS */
877 Tss->LDT = 0;
878 Tss->Ss0 = KGDT_R0_DATA;
879 }
880
881 VOID
882 FASTCALL
883 INIT_FUNCTION
884 Ki386InitializeTss(IN PKTSS Tss,
885 IN PKIDTENTRY Idt,
886 IN PKGDTENTRY Gdt)
887 {
888 PKGDTENTRY TssEntry, TaskGateEntry;
889
890 /* Initialize the boot TSS. */
891 TssEntry = &Gdt[KGDT_TSS / sizeof(KGDTENTRY)];
892 TssEntry->HighWord.Bits.Type = I386_TSS;
893 TssEntry->HighWord.Bits.Pres = 1;
894 TssEntry->HighWord.Bits.Dpl = 0;
895 KiInitializeTSS2(Tss, TssEntry);
896 KiInitializeTSS(Tss);
897
898 /* Load the task register */
899 Ke386SetTr(KGDT_TSS);
900
901 /* Setup the Task Gate for Double Fault Traps */
902 TaskGateEntry = (PKGDTENTRY)&Idt[8];
903 TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
904 TaskGateEntry->HighWord.Bits.Pres = 1;
905 TaskGateEntry->HighWord.Bits.Dpl = 0;
906 ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_DF_TSS;
907
908 /* Initialize the TSS used for handling double faults. */
909 Tss = (PKTSS)KiDoubleFaultTSS;
910 KiInitializeTSS(Tss);
911 Tss->CR3 = __readcr3();
912 Tss->Esp0 = KiDoubleFaultStack;
913 Tss->Esp = KiDoubleFaultStack;
914 Tss->Eip = PtrToUlong(KiTrap08);
915 Tss->Cs = KGDT_R0_CODE;
916 Tss->Fs = KGDT_R0_PCR;
917 Tss->Ss = Ke386GetSs();
918 Tss->Es = KGDT_R3_DATA | RPL_MASK;
919 Tss->Ds = KGDT_R3_DATA | RPL_MASK;
920
921 /* Setup the Double Trap TSS entry in the GDT */
922 TssEntry = &Gdt[KGDT_DF_TSS / sizeof(KGDTENTRY)];
923 TssEntry->HighWord.Bits.Type = I386_TSS;
924 TssEntry->HighWord.Bits.Pres = 1;
925 TssEntry->HighWord.Bits.Dpl = 0;
926 TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
927 TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
928 TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
929 TssEntry->LimitLow = KTSS_IO_MAPS;
930
931 /* Now setup the NMI Task Gate */
932 TaskGateEntry = (PKGDTENTRY)&Idt[2];
933 TaskGateEntry->HighWord.Bits.Type = I386_TASK_GATE;
934 TaskGateEntry->HighWord.Bits.Pres = 1;
935 TaskGateEntry->HighWord.Bits.Dpl = 0;
936 ((PKIDTENTRY)TaskGateEntry)->Selector = KGDT_NMI_TSS;
937
938 /* Initialize the actual TSS */
939 Tss = (PKTSS)KiNMITSS;
940 KiInitializeTSS(Tss);
941 Tss->CR3 = __readcr3();
942 Tss->Esp0 = KiDoubleFaultStack;
943 Tss->Esp = KiDoubleFaultStack;
944 Tss->Eip = PtrToUlong(KiTrap02);
945 Tss->Cs = KGDT_R0_CODE;
946 Tss->Fs = KGDT_R0_PCR;
947 Tss->Ss = Ke386GetSs();
948 Tss->Es = KGDT_R3_DATA | RPL_MASK;
949 Tss->Ds = KGDT_R3_DATA | RPL_MASK;
950
951 /* And its associated TSS Entry */
952 TssEntry = &Gdt[KGDT_NMI_TSS / sizeof(KGDTENTRY)];
953 TssEntry->HighWord.Bits.Type = I386_TSS;
954 TssEntry->HighWord.Bits.Pres = 1;
955 TssEntry->HighWord.Bits.Dpl = 0;
956 TssEntry->BaseLow = (USHORT)((ULONG_PTR)Tss & 0xFFFF);
957 TssEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Tss >> 16);
958 TssEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Tss >> 24);
959 TssEntry->LimitLow = KTSS_IO_MAPS;
960 }
961
962 VOID
963 NTAPI
964 KeFlushCurrentTb(VOID)
965 {
966 /* Flush the TLB by resetting CR3 */
967 __writecr3(__readcr3());
968 }
969
970 VOID
971 NTAPI
972 KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState)
973 {
974 PKGDTENTRY TssEntry;
975
976 //
977 // Restore the CR registers
978 //
979 __writecr0(ProcessorState->SpecialRegisters.Cr0);
980 Ke386SetCr2(ProcessorState->SpecialRegisters.Cr2);
981 __writecr3(ProcessorState->SpecialRegisters.Cr3);
982 if (KeFeatureBits & KF_CR4) __writecr4(ProcessorState->SpecialRegisters.Cr4);
983
984 //
985 // Restore the DR registers
986 //
987 __writedr(0, ProcessorState->SpecialRegisters.KernelDr0);
988 __writedr(1, ProcessorState->SpecialRegisters.KernelDr1);
989 __writedr(2, ProcessorState->SpecialRegisters.KernelDr2);
990 __writedr(3, ProcessorState->SpecialRegisters.KernelDr3);
991 __writedr(6, ProcessorState->SpecialRegisters.KernelDr6);
992 __writedr(7, ProcessorState->SpecialRegisters.KernelDr7);
993
994 //
995 // Restore GDT and IDT
996 //
997 Ke386SetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
998 __lidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
999
1000 //
1001 // Clear the busy flag so we don't crash if we reload the same selector
1002 //
1003 TssEntry = (PKGDTENTRY)(ProcessorState->SpecialRegisters.Gdtr.Base +
1004 ProcessorState->SpecialRegisters.Tr);
1005 TssEntry->HighWord.Bytes.Flags1 &= ~0x2;
1006
1007 //
1008 // Restore TSS and LDT
1009 //
1010 Ke386SetTr(ProcessorState->SpecialRegisters.Tr);
1011 Ke386SetLocalDescriptorTable(ProcessorState->SpecialRegisters.Ldtr);
1012 }
1013
1014 VOID
1015 NTAPI
1016 KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState)
1017 {
1018 /* Save the CR registers */
1019 ProcessorState->SpecialRegisters.Cr0 = __readcr0();
1020 ProcessorState->SpecialRegisters.Cr2 = __readcr2();
1021 ProcessorState->SpecialRegisters.Cr3 = __readcr3();
1022 ProcessorState->SpecialRegisters.Cr4 = (KeFeatureBits & KF_CR4) ?
1023 __readcr4() : 0;
1024
1025 /* Save the DR registers */
1026 ProcessorState->SpecialRegisters.KernelDr0 = __readdr(0);
1027 ProcessorState->SpecialRegisters.KernelDr1 = __readdr(1);
1028 ProcessorState->SpecialRegisters.KernelDr2 = __readdr(2);
1029 ProcessorState->SpecialRegisters.KernelDr3 = __readdr(3);
1030 ProcessorState->SpecialRegisters.KernelDr6 = __readdr(6);
1031 ProcessorState->SpecialRegisters.KernelDr7 = __readdr(7);
1032 __writedr(7, 0);
1033
1034 /* Save GDT, IDT, LDT and TSS */
1035 Ke386GetGlobalDescriptorTable(&ProcessorState->SpecialRegisters.Gdtr.Limit);
1036 __sidt(&ProcessorState->SpecialRegisters.Idtr.Limit);
1037 ProcessorState->SpecialRegisters.Tr = Ke386GetTr();
1038 ProcessorState->SpecialRegisters.Ldtr = Ke386GetLocalDescriptorTable();
1039 }
1040
1041 VOID
1042 NTAPI
1043 INIT_FUNCTION
1044 KiInitializeMachineType(VOID)
1045 {
1046 /* Set the Machine Type we got from NTLDR */
1047 KeI386MachineType = KeLoaderBlock->u.I386.MachineType & 0x000FF;
1048 }
1049
1050 ULONG_PTR
1051 NTAPI
1052 INIT_FUNCTION
1053 KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
1054 {
1055 /* Set CS and ESP */
1056 WRMSR(0x174, KGDT_R0_CODE);
1057 WRMSR(0x175, (ULONG_PTR)KeGetCurrentPrcb()->DpcStack);
1058
1059 /* Set LSTAR */
1060 WRMSR(0x176, (ULONG_PTR)KiFastCallEntry);
1061 return 0;
1062 }
1063
1064 VOID
1065 NTAPI
1066 INIT_FUNCTION
1067 KiRestoreFastSyscallReturnState(VOID)
1068 {
1069 /* Check if the CPU Supports fast system call */
1070 if (KeFeatureBits & KF_FAST_SYSCALL)
1071 {
1072 /* Check if it has been disabled */
1073 if (!KiFastSystemCallDisable)
1074 {
1075 /* Do an IPI to enable it */
1076 KeIpiGenericCall(KiLoadFastSyscallMachineSpecificRegisters, 0);
1077
1078 /* It's enabled, so use the proper exit stub */
1079 KiFastCallExitHandler = KiSystemCallSysExitReturn;
1080 DPRINT1("Support for SYSENTER detected.\n");
1081 }
1082 else
1083 {
1084 /* Disable fast system call */
1085 KeFeatureBits &= ~KF_FAST_SYSCALL;
1086 KiFastCallExitHandler = KiSystemCallTrapReturn;
1087 DPRINT1("Support for SYSENTER disabled.\n");
1088 }
1089 }
1090 else
1091 {
1092 /* Use the IRET handler */
1093 KiFastCallExitHandler = KiSystemCallTrapReturn;
1094 DPRINT1("No support for SYSENTER detected.\n");
1095 }
1096 }
1097
1098 ULONG_PTR
1099 NTAPI
1100 INIT_FUNCTION
1101 Ki386EnableDE(IN ULONG_PTR Context)
1102 {
1103 /* Enable DE */
1104 __writecr4(__readcr4() | CR4_DE);
1105 return 0;
1106 }
1107
1108 ULONG_PTR
1109 NTAPI
1110 INIT_FUNCTION
1111 Ki386EnableFxsr(IN ULONG_PTR Context)
1112 {
1113 /* Enable FXSR */
1114 __writecr4(__readcr4() | CR4_FXSR);
1115 return 0;
1116 }
1117
1118 ULONG_PTR
1119 NTAPI
1120 INIT_FUNCTION
1121 Ki386EnableXMMIExceptions(IN ULONG_PTR Context)
1122 {
1123 PKIDTENTRY IdtEntry;
1124
1125 /* Get the IDT Entry for Interrupt 0x13 */
1126 IdtEntry = &((PKIPCR)KeGetPcr())->IDT[0x13];
1127
1128 /* Set it up */
1129 IdtEntry->Selector = KGDT_R0_CODE;
1130 IdtEntry->Offset = ((ULONG_PTR)KiTrap13 & 0xFFFF);
1131 IdtEntry->ExtendedOffset = ((ULONG_PTR)KiTrap13 >> 16) & 0xFFFF;
1132 ((PKIDT_ACCESS)&IdtEntry->Access)->Dpl = 0;
1133 ((PKIDT_ACCESS)&IdtEntry->Access)->Present = 1;
1134 ((PKIDT_ACCESS)&IdtEntry->Access)->SegmentType = I386_INTERRUPT_GATE;
1135
1136 /* Enable XMMI exceptions */
1137 __writecr4(__readcr4() | CR4_XMMEXCPT);
1138 return 0;
1139 }
1140
1141 VOID
1142 NTAPI
1143 INIT_FUNCTION
1144 KiI386PentiumLockErrataFixup(VOID)
1145 {
1146 KDESCRIPTOR IdtDescriptor;
1147 PKIDTENTRY NewIdt, NewIdt2;
1148
1149 /* Allocate memory for a new IDT */
1150 NewIdt = ExAllocatePool(NonPagedPool, 2 * PAGE_SIZE);
1151
1152 /* Put everything after the first 7 entries on a new page */
1153 NewIdt2 = (PVOID)((ULONG_PTR)NewIdt + PAGE_SIZE - (7 * sizeof(KIDTENTRY)));
1154
1155 /* Disable interrupts */
1156 _disable();
1157
1158 /* Get the current IDT and copy it */
1159 __sidt(&IdtDescriptor.Limit);
1160 RtlCopyMemory(NewIdt2,
1161 (PVOID)IdtDescriptor.Base,
1162 IdtDescriptor.Limit + 1);
1163 IdtDescriptor.Base = (ULONG)NewIdt2;
1164
1165 /* Set the new IDT */
1166 __lidt(&IdtDescriptor.Limit);
1167 ((PKIPCR)KeGetPcr())->IDT = NewIdt2;
1168
1169 /* Restore interrupts */
1170 _enable();
1171
1172 /* Set the first 7 entries as read-only to produce a fault */
1173 MmSetPageProtect(NULL, NewIdt, PAGE_READONLY);
1174 }
1175
1176 BOOLEAN
1177 NTAPI
1178 KeDisableInterrupts(VOID)
1179 {
1180 ULONG Flags;
1181 BOOLEAN Return;
1182
1183 /* Get EFLAGS and check if the interrupt bit is set */
1184 Flags = __readeflags();
1185 Return = (Flags & EFLAGS_INTERRUPT_MASK) ? TRUE: FALSE;
1186
1187 /* Disable interrupts */
1188 _disable();
1189 return Return;
1190 }
1191
1192 BOOLEAN
1193 NTAPI
1194 KeInvalidateAllCaches(VOID)
1195 {
1196 /* Only supported on Pentium Pro and higher */
1197 if (KeI386CpuType < 6) return FALSE;
1198
1199 /* Invalidate all caches */
1200 __wbinvd();
1201 return TRUE;
1202 }
1203
1204 VOID
1205 FASTCALL
1206 KeZeroPages(IN PVOID Address,
1207 IN ULONG Size)
1208 {
1209 /* Not using XMMI in this routine */
1210 RtlZeroMemory(Address, Size);
1211 }
1212
1213 VOID
1214 NTAPI
1215 KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame,
1216 IN PKEXCEPTION_FRAME ExceptionFrame)
1217 {
1218 PKPRCB Prcb = KeGetCurrentPrcb();
1219
1220 //
1221 // Save full context
1222 //
1223 Prcb->ProcessorState.ContextFrame.ContextFlags = CONTEXT_FULL |
1224 CONTEXT_DEBUG_REGISTERS;
1225 KeTrapFrameToContext(TrapFrame, NULL, &Prcb->ProcessorState.ContextFrame);
1226
1227 //
1228 // Save control registers
1229 //
1230 KiSaveProcessorControlState(&Prcb->ProcessorState);
1231 }
1232
1233 BOOLEAN
1234 NTAPI
1235 INIT_FUNCTION
1236 KiIsNpxPresent(VOID)
1237 {
1238 ULONG Cr0;
1239 USHORT Magic;
1240
1241 /* Set magic */
1242 Magic = 0xFFFF;
1243
1244 /* Read CR0 and mask out FPU flags */
1245 Cr0 = __readcr0() & ~(CR0_MP | CR0_TS | CR0_EM | CR0_ET);
1246
1247 /* Store on FPU stack */
1248 #ifdef _MSC_VER
1249 __asm fninit;
1250 __asm fnstsw Magic;
1251 #else
1252 asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic));
1253 #endif
1254
1255 /* Magic should now be cleared */
1256 if (Magic & 0xFF)
1257 {
1258 /* You don't have an FPU -- enable emulation for now */
1259 __writecr0(Cr0 | CR0_EM | CR0_TS);
1260 return FALSE;
1261 }
1262
1263 /* You have an FPU, enable it */
1264 Cr0 |= CR0_ET;
1265
1266 /* Enable INT 16 on 486 and higher */
1267 if (KeGetCurrentPrcb()->CpuType >= 3) Cr0 |= CR0_NE;
1268
1269 /* Set FPU state */
1270 __writecr0(Cr0 | CR0_EM | CR0_TS);
1271 return TRUE;
1272 }
1273
1274 BOOLEAN
1275 NTAPI
1276 INIT_FUNCTION
1277 KiIsNpxErrataPresent(VOID)
1278 {
1279 BOOLEAN ErrataPresent;
1280 ULONG Cr0;
1281 volatile double Value1, Value2;
1282
1283 /* Disable interrupts */
1284 _disable();
1285
1286 /* Read CR0 and remove FPU flags */
1287 Cr0 = __readcr0();
1288 __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1289
1290 /* Initialize FPU state */
1291 Ke386FnInit();
1292
1293 /* Multiply the magic values and divide, we should get the result back */
1294 Value1 = 4195835.0;
1295 Value2 = 3145727.0;
1296 ErrataPresent = (Value1 * Value2 / 3145727.0) != 4195835.0;
1297
1298 /* Restore CR0 */
1299 __writecr0(Cr0);
1300
1301 /* Enable interrupts */
1302 _enable();
1303
1304 /* Return if there's an errata */
1305 return ErrataPresent;
1306 }
1307
1308 VOID
1309 NTAPI
1310 KiFlushNPXState(IN PFLOATING_SAVE_AREA SaveArea)
1311 {
1312 ULONG EFlags, Cr0;
1313 PKTHREAD Thread, NpxThread;
1314 PFX_SAVE_AREA FxSaveArea;
1315
1316 /* Save volatiles and disable interrupts */
1317 EFlags = __readeflags();
1318 _disable();
1319
1320 /* Save the PCR and get the current thread */
1321 Thread = KeGetCurrentThread();
1322
1323 /* Check if we're already loaded */
1324 if (Thread->NpxState != NPX_STATE_LOADED)
1325 {
1326 /* If there's nothing to load, quit */
1327 if (!SaveArea) return;
1328
1329 /* Need FXSR support for this */
1330 ASSERT(KeI386FxsrPresent == TRUE);
1331
1332 /* Check for sane CR0 */
1333 Cr0 = __readcr0();
1334 if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
1335 {
1336 /* Mask out FPU flags */
1337 __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1338 }
1339
1340 /* Get the NPX thread and check its FPU state */
1341 NpxThread = KeGetCurrentPrcb()->NpxThread;
1342 if ((NpxThread) && (NpxThread->NpxState == NPX_STATE_LOADED))
1343 {
1344 /* Get the FX frame and store the state there */
1345 FxSaveArea = KiGetThreadNpxArea(NpxThread);
1346 Ke386FxSave(FxSaveArea);
1347
1348 /* NPX thread has lost its state */
1349 NpxThread->NpxState = NPX_STATE_NOT_LOADED;
1350 }
1351
1352 /* Now load NPX state from the NPX area */
1353 FxSaveArea = KiGetThreadNpxArea(Thread);
1354 Ke386FxStore(FxSaveArea);
1355 }
1356 else
1357 {
1358 /* Check for sane CR0 */
1359 Cr0 = __readcr0();
1360 if (Cr0 & (CR0_MP | CR0_TS | CR0_EM))
1361 {
1362 /* Mask out FPU flags */
1363 __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM));
1364 }
1365
1366 /* Get FX frame */
1367 FxSaveArea = KiGetThreadNpxArea(Thread);
1368 Thread->NpxState = NPX_STATE_NOT_LOADED;
1369
1370 /* Save state if supported by CPU */
1371 if (KeI386FxsrPresent) Ke386FxSave(FxSaveArea);
1372 }
1373
1374 /* Now save the FN state wherever it was requested */
1375 if (SaveArea) Ke386FnSave(SaveArea);
1376
1377 /* Clear NPX thread */
1378 KeGetCurrentPrcb()->NpxThread = NULL;
1379
1380 /* Add the CR0 from the NPX frame */
1381 Cr0 |= NPX_STATE_NOT_LOADED;
1382 Cr0 |= FxSaveArea->Cr0NpxState;
1383 __writecr0(Cr0);
1384
1385 /* Restore interrupt state */
1386 __writeeflags(EFlags);
1387 }
1388
1389 /* PUBLIC FUNCTIONS **********************************************************/
1390
1391 /*
1392 * @implemented
1393 */
1394 VOID
1395 NTAPI
1396 KiCoprocessorError(VOID)
1397 {
1398 PFX_SAVE_AREA NpxArea;
1399
1400 /* Get the FPU area */
1401 NpxArea = KiGetThreadNpxArea(KeGetCurrentThread());
1402
1403 /* Set CR0_TS */
1404 NpxArea->Cr0NpxState = CR0_TS;
1405 __writecr0(__readcr0() | CR0_TS);
1406 }
1407
1408 /*
1409 * @implemented
1410 */
1411 NTSTATUS
1412 NTAPI
1413 KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save)
1414 {
1415 PFNSAVE_FORMAT FpState;
1416 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
1417 DPRINT1("%s is not really implemented\n", __FUNCTION__);
1418
1419 /* check if we are doing software emulation */
1420 if (!KeI386NpxPresent) return STATUS_ILLEGAL_FLOAT_CONTEXT;
1421
1422 FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT));
1423 if (!FpState) return STATUS_INSUFFICIENT_RESOURCES;
1424
1425 *((PVOID *) Save) = FpState;
1426 #ifdef __GNUC__
1427 asm volatile("fnsave %0\n\t" : "=m" (*FpState));
1428 #else
1429 __asm
1430 {
1431 fnsave [FpState]
1432 };
1433 #endif
1434
1435 KeGetCurrentThread()->DispatcherHeader.NpxIrql = KeGetCurrentIrql();
1436 return STATUS_SUCCESS;
1437 }
1438
1439 /*
1440 * @implemented
1441 */
1442 NTSTATUS
1443 NTAPI
1444 KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save)
1445 {
1446 PFNSAVE_FORMAT FpState = *((PVOID *) Save);
1447 ASSERT(KeGetCurrentThread()->DispatcherHeader.NpxIrql == KeGetCurrentIrql());
1448 DPRINT1("%s is not really implemented\n", __FUNCTION__);
1449
1450 #ifdef __GNUC__
1451 asm volatile("fnclex\n\t");
1452 asm volatile("frstor %0\n\t" : "=m" (*FpState));
1453 #else
1454 __asm
1455 {
1456 fnclex
1457 frstor [FpState]
1458 };
1459 #endif
1460
1461 ExFreePool(FpState);
1462 return STATUS_SUCCESS;
1463 }
1464
1465 /*
1466 * @implemented
1467 */
1468 ULONG
1469 NTAPI
1470 KeGetRecommendedSharedDataAlignment(VOID)
1471 {
1472 /* Return the global variable */
1473 return KeLargestCacheLine;
1474 }
1475
1476 VOID
1477 NTAPI
1478 KiFlushTargetEntireTb(IN PKIPI_CONTEXT PacketContext,
1479 IN PVOID Ignored1,
1480 IN PVOID Ignored2,
1481 IN PVOID Ignored3)
1482 {
1483 /* Signal this packet as done */
1484 KiIpiSignalPacketDone(PacketContext);
1485
1486 /* Flush the TB for the Current CPU */
1487 KeFlushCurrentTb();
1488 }
1489
1490 /*
1491 * @implemented
1492 */
1493 VOID
1494 NTAPI
1495 KeFlushEntireTb(IN BOOLEAN Invalid,
1496 IN BOOLEAN AllProcessors)
1497 {
1498 KIRQL OldIrql;
1499 #ifdef CONFIG_SMP
1500 KAFFINITY TargetAffinity;
1501 PKPRCB Prcb = KeGetCurrentPrcb();
1502 #endif
1503
1504 /* Raise the IRQL for the TB Flush */
1505 OldIrql = KeRaiseIrqlToSynchLevel();
1506
1507 #ifdef CONFIG_SMP
1508 /* FIXME: Use KiTbFlushTimeStamp to synchronize TB flush */
1509
1510 /* Get the current processor affinity, and exclude ourselves */
1511 TargetAffinity = KeActiveProcessors;
1512 TargetAffinity &= ~Prcb->SetMember;
1513
1514 /* Make sure this is MP */
1515 if (TargetAffinity)
1516 {
1517 /* Send an IPI TB flush to the other processors */
1518 KiIpiSendPacket(TargetAffinity,
1519 KiFlushTargetEntireTb,
1520 NULL,
1521 0,
1522 NULL);
1523 }
1524 #endif
1525
1526 /* Flush the TB for the Current CPU, and update the flush stamp */
1527 KeFlushCurrentTb();
1528
1529 #ifdef CONFIG_SMP
1530 /* If this is MP, wait for the other processors to finish */
1531 if (TargetAffinity)
1532 {
1533 /* Sanity check */
1534 ASSERT(Prcb == (volatile PKPRCB)KeGetCurrentPrcb());
1535
1536 /* FIXME: TODO */
1537 ASSERTMSG("Not yet implemented\n", FALSE);
1538 }
1539 #endif
1540
1541 /* Update the flush stamp and return to original IRQL */
1542 InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1);
1543 KeLowerIrql(OldIrql);
1544 }
1545
1546 /*
1547 * @implemented
1548 */
1549 VOID
1550 NTAPI
1551 KeSetDmaIoCoherency(IN ULONG Coherency)
1552 {
1553 /* Save the coherency globally */
1554 KiDmaIoCoherency = Coherency;
1555 }
1556
1557 /*
1558 * @implemented
1559 */
1560 KAFFINITY
1561 NTAPI
1562 KeQueryActiveProcessors(VOID)
1563 {
1564 PAGED_CODE();
1565
1566 /* Simply return the number of active processors */
1567 return KeActiveProcessors;
1568 }
1569
1570 /*
1571 * @implemented
1572 */
1573 VOID
1574 __cdecl
1575 KeSaveStateForHibernate(IN PKPROCESSOR_STATE State)
1576 {
1577 /* Capture the context */
1578 RtlCaptureContext(&State->ContextFrame);
1579
1580 /* Capture the control state */
1581 KiSaveProcessorControlState(State);
1582 }