detect hyper-threading, determine number of logical processors per phyiscal processor...
[reactos.git] / reactos / ntoskrnl / ke / i386 / kernel.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/i386/kernel.c
6 * PURPOSE: Initializes the kernel
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 ULONG KiPcrInitDone = 0;
20 static ULONG PcrsAllocated = 0;
21 static ULONG Ke386CpuidFlags2, Ke386CpuidExFlags, Ke386CpuidExMisc;
22 ULONG Ke386CacheAlignment;
23 CHAR Ke386CpuidModel[49] = {0,};
24 ULONG Ke386L1CacheSize;
25 BOOLEAN Ke386NoExecute = FALSE;
26 BOOLEAN Ke386Pae = FALSE;
27 BOOLEAN Ke386GlobalPagesEnabled = FALSE;
28 ULONG KiFastSystemCallDisable = 1;
29
30 /* FUNCTIONS *****************************************************************/
31
32 VOID INIT_FUNCTION STATIC
33 Ki386GetCpuId(VOID)
34 {
35 ULONG OrigFlags, Flags, FinalFlags;
36 ULONG MaxCpuidLevel;
37 ULONG Dummy, Eax, Ecx, Edx;
38 PKPCR Pcr = KeGetCurrentKPCR();
39
40 Ke386CpuidFlags2 = Ke386CpuidExFlags = 0;
41 Ke386CacheAlignment = 32;
42
43 /* Try to toggle the id bit in eflags. */
44 Ke386SaveFlags(OrigFlags);
45 Flags = OrigFlags ^ X86_EFLAGS_ID;
46 Ke386RestoreFlags(Flags);
47 Ke386SaveFlags(FinalFlags);
48
49 Pcr->PrcbData.LogicalProcessorsPerPhysicalProcessor = 1;
50 Pcr->PrcbData.InitialApicId = 0xff;
51
52 if ((OrigFlags & X86_EFLAGS_ID) == (FinalFlags & X86_EFLAGS_ID))
53 {
54 /* No cpuid supported. */
55 Pcr->PrcbData.CpuID = FALSE;
56 Pcr->PrcbData.CpuType = 3;
57 return;
58 }
59 Pcr->PrcbData.CpuID = TRUE;
60
61 /* Get the vendor name and the maximum cpuid level supported. */
62 Ki386Cpuid(0, &MaxCpuidLevel, (PULONG)&Pcr->PrcbData.VendorString[0], (PULONG)&Pcr->PrcbData.VendorString[8], (PULONG)&Pcr->PrcbData.VendorString[4]);
63 if (MaxCpuidLevel > 0)
64 {
65 /* Get the feature flags. */
66 Ki386Cpuid(1, &Eax, &Ke386CpuidExMisc, &Ke386CpuidFlags2, &Pcr->PrcbData.FeatureBits);
67 /* Get the cache alignment, if it is available */
68 if (Pcr->PrcbData.FeatureBits & (1<<19))
69 {
70 Ke386CacheAlignment = ((Ke386CpuidExMisc >> 8) & 0xff) * 8;
71 }
72 Pcr->PrcbData.CpuType = (Eax >> 8) & 0xf;
73 Pcr->PrcbData.CpuStep = (Eax & 0xf) | ((Eax << 4) & 0xf00);
74
75 Pcr->PrcbData.InitialApicId = (Ke386CpuidExMisc >> 24) & 0xff;
76
77 /* detect Hyper-Threading on Pentium 4 CPUs or later */
78 if ((Pcr->PrcbData.CpuType == 0xf || (Eax & 0x0f00000)) &&
79 !strncmp(Pcr->PrcbData.VendorString, "GenuineIntel", 12) &&
80 Pcr->PrcbData.FeatureBits & X86_FEATURE_HT)
81 {
82 Pcr->PrcbData.LogicalProcessorsPerPhysicalProcessor = (Ke386CpuidExMisc >> 16) & 0xff;
83 }
84 }
85 else
86 {
87 Pcr->PrcbData.CpuType = 4;
88 }
89
90 /* Get the maximum extended cpuid level supported. */
91 Ki386Cpuid(0x80000000, &MaxCpuidLevel, &Dummy, &Dummy, &Dummy);
92 if (MaxCpuidLevel > 0)
93 {
94 /* Get the extended feature flags. */
95 Ki386Cpuid(0x80000001, &Dummy, &Dummy, &Dummy, &Ke386CpuidExFlags);
96 }
97
98 /* Get the model name. */
99 if (MaxCpuidLevel >= 0x80000004)
100 {
101 PULONG v = (PULONG)Ke386CpuidModel;
102 Ki386Cpuid(0x80000002, v, v + 1, v + 2, v + 3);
103 Ki386Cpuid(0x80000003, v + 4, v + 5, v + 6, v + 7);
104 Ki386Cpuid(0x80000004, v + 8, v + 9, v + 10, v + 11);
105 }
106
107 /* Get the L1 cache size */
108 if (MaxCpuidLevel >= 0x80000005)
109 {
110 Ki386Cpuid(0x80000005, &Dummy, &Dummy, &Ecx, &Edx);
111 Ke386L1CacheSize = (Ecx >> 24)+(Edx >> 24);
112 if ((Ecx & 0xff) > 0)
113 {
114 Ke386CacheAlignment = Ecx & 0xff;
115 }
116 }
117
118 /* Get the L2 cache size */
119 if (MaxCpuidLevel >= 0x80000006)
120 {
121 Ki386Cpuid(0x80000006, &Dummy, &Dummy, &Ecx, &Dummy);
122 Pcr->L2CacheSize = Ecx >> 16;
123 }
124 }
125
126 VOID INIT_FUNCTION
127 KePrepareForApplicationProcessorInit(ULONG Id)
128 {
129 DPRINT("KePrepareForApplicationProcessorInit(Id %d)\n", Id);
130 PFN_TYPE PrcPfn;
131 PKPCR Pcr;
132 PKPCR BootPcr;
133
134 BootPcr = (PKPCR)KPCR_BASE;
135 Pcr = (PKPCR)((ULONG_PTR)KPCR_BASE + Id * PAGE_SIZE);
136
137 MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &PrcPfn);
138 MmCreateVirtualMappingForKernel((PVOID)Pcr,
139 PAGE_READWRITE,
140 &PrcPfn,
141 1);
142 /*
143 * Create a PCR for this processor
144 */
145 memset(Pcr, 0, PAGE_SIZE);
146 Pcr->ProcessorNumber = Id;
147 Pcr->Tib.Self = &Pcr->Tib;
148 Pcr->Self = Pcr;
149 Pcr->Irql = SYNCH_LEVEL;
150
151 Pcr->PrcbData.MHz = BootPcr->PrcbData.MHz;
152 Pcr->StallScaleFactor = BootPcr->StallScaleFactor;
153
154 /* Mark the end of the exception handler list */
155 Pcr->Tib.ExceptionList = (PVOID)-1;
156
157 KiGdtPrepareForApplicationProcessorInit(Id);
158 }
159
160 VOID
161 KeApplicationProcessorInit(VOID)
162 {
163 ULONG Offset;
164 PKPCR Pcr;
165
166 DPRINT("KeApplicationProcessorInit()\n");
167
168 if (Ke386GlobalPagesEnabled)
169 {
170 /* Enable global pages */
171 Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
172 }
173
174
175 Offset = InterlockedIncrementUL(&PcrsAllocated) - 1;
176 Pcr = (PKPCR)((ULONG_PTR)KPCR_BASE + Offset * PAGE_SIZE);
177
178 /*
179 * Initialize the GDT
180 */
181 KiInitializeGdt(Pcr);
182
183 /* Get processor information. */
184 Ki386GetCpuId();
185
186 /* Check FPU/MMX/SSE support. */
187 KiCheckFPU();
188
189 KeInitDpc(Pcr);
190
191 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_SYSCALL)
192 {
193 extern void KiFastCallEntry(void);
194
195 /* CS Selector of the target segment. */
196 Ke386Wrmsr(0x174, KERNEL_CS, 0);
197 /* Target ESP. */
198 Ke386Wrmsr(0x175, 0, 0);
199 /* Target EIP. */
200 Ke386Wrmsr(0x176, (ULONG_PTR)KiFastCallEntry, 0);
201 }
202
203 /*
204 * It is now safe to process interrupts
205 */
206 KeLowerIrql(DISPATCH_LEVEL);
207
208 /*
209 * Initialize the TSS
210 */
211 Ki386ApplicationProcessorInitializeTSS();
212
213 /*
214 * Initialize a default LDT
215 */
216 Ki386InitializeLdt();
217
218 /* Now we can enable interrupts. */
219 Ke386EnableInterrupts();
220 }
221
222 VOID INIT_FUNCTION
223 KeInit1(PCHAR CommandLine, PULONG LastKernelAddress)
224 {
225 PKPCR KPCR;
226 BOOLEAN Pae = FALSE;
227 BOOLEAN NoExecute = FALSE;
228 PCHAR p1, p2;
229 extern USHORT KiBootGdt[];
230 extern KTSS KiBootTss;
231
232 /*
233 * Initialize the initial PCR region. We can't allocate a page
234 * with MmAllocPage() here because MmInit1() has not yet been
235 * called, so we use a predefined page in low memory
236 */
237
238 KPCR = (PKPCR)KPCR_BASE;
239 memset(KPCR, 0, PAGE_SIZE);
240 KPCR->Self = KPCR;
241 KPCR->Irql = SYNCH_LEVEL;
242 KPCR->Tib.Self = &KPCR->Tib;
243 KPCR->GDT = KiBootGdt;
244 KPCR->IDT = (PUSHORT)KiIdt;
245 KPCR->TSS = &KiBootTss;
246 KPCR->ProcessorNumber = 0;
247 KiPcrInitDone = 1;
248 PcrsAllocated++;
249
250 KiInitializeGdt (NULL);
251 Ki386BootInitializeTSS();
252 Ki386InitializeLdt();
253
254 /* Get processor information. */
255 Ki386GetCpuId();
256
257 /* Check FPU/MMX/SSE support. */
258 KiCheckFPU();
259
260 /* Mark the end of the exception handler list */
261 KPCR->Tib.ExceptionList = (PVOID)-1;
262
263 KeInitDpc(KPCR);
264
265 KeInitExceptions ();
266 KeInitInterrupts ();
267
268 if (KPCR->PrcbData.FeatureBits & X86_FEATURE_PGE)
269 {
270 ULONG Flags;
271 /* Enable global pages */
272 Ke386GlobalPagesEnabled = TRUE;
273 Ke386SaveFlags(Flags);
274 Ke386DisableInterrupts();
275 Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
276 Ke386RestoreFlags(Flags);
277 }
278
279 /* Search for pae and noexecute */
280 p1 = (PCHAR)KeLoaderBlock.CommandLine;
281 while(*p1 && (p2 = strchr(p1, '/')))
282 {
283 p2++;
284 if (!_strnicmp(p2, "PAE", 3))
285 {
286 if (p2[3] == ' ' || p2[3] == 0)
287 {
288 p2 += 3;
289 Pae = TRUE;
290 }
291 }
292 else if (!_strnicmp(p2, "NOEXECUTE", 9))
293 {
294 if (p2[9] == ' ' || p2[9] == '=' || p2[9] == 0)
295 {
296 p2 += 9;
297 NoExecute = TRUE;
298 }
299 }
300 p1 = p2;
301 }
302 #if 0
303 /*
304 * FIXME:
305 * Make the detection of the noexecute feature more portable.
306 */
307 if(KPCR->PrcbData.CpuType == 0xf &&
308 RtlCompareMemory("AuthenticAMD", KPCR->PrcbData.VendorString, 12) == 12)
309 {
310 if (NoExecute)
311 {
312 ULONG Flags, l, h;
313 Ke386SaveFlags(Flags);
314 Ke386DisableInterrupts();
315
316 Ke386Rdmsr(0xc0000080, l, h);
317 l |= (1 << 11);
318 Ke386Wrmsr(0xc0000080, l, h);
319 Ke386NoExecute = TRUE;
320 Ke386RestoreFlags(Flags);
321 }
322 }
323 else
324 {
325 NoExecute=FALSE;
326 }
327 #endif
328
329 Ke386Pae = Ke386GetCr4() & X86_CR4_PAE ? TRUE : FALSE;
330 #if 0
331 /* Enable PAE mode */
332 if ((Pae && (KPCR->PrcbData.FeatureBits & X86_FEATURE_PAE)) || NoExecute)
333 {
334 MiEnablePAE((PVOID*)LastKernelAddress);
335 Ke386PaeEnabled = TRUE;
336 }
337 #endif
338 if (KPCR->PrcbData.FeatureBits & X86_FEATURE_SYSCALL)
339 {
340 extern void KiFastCallEntry(void);
341
342 /* CS Selector of the target segment. */
343 Ke386Wrmsr(0x174, KERNEL_CS, 0);
344 /* Target ESP. */
345 Ke386Wrmsr(0x175, 0, 0);
346 /* Target EIP. */
347 Ke386Wrmsr(0x176, (ULONG_PTR)KiFastCallEntry, 0);
348 }
349 }
350
351 VOID INIT_FUNCTION
352 KeInit2(VOID)
353 {
354 PKPCR Pcr = KeGetCurrentKPCR();
355
356 KeInitializeBugCheck();
357 KeInitializeDispatcher();
358 KiInitializeSystemClock();
359
360 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_PAE)
361 {
362 DPRINT("CPU supports PAE mode\n");
363 if (Ke386Pae)
364 {
365 DPRINT("CPU runs in PAE mode\n");
366 if (Ke386NoExecute)
367 {
368 DPRINT("NoExecute is enabled\n");
369 }
370 }
371 else
372 {
373 DPRINT("CPU doesn't run in PAE mode\n");
374 }
375 }
376 if ((Pcr->PrcbData.FeatureBits & (X86_FEATURE_FXSR | X86_FEATURE_MMX | X86_FEATURE_SSE | X86_FEATURE_SSE2)) ||
377 (Ke386CpuidFlags2 & X86_EXT_FEATURE_SSE3))
378 {
379 DPRINT("CPU supports" "%s%s%s%s%s" ".\n",
380 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_FXSR) ? " FXSR" : ""),
381 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_MMX) ? " MMX" : ""),
382 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE) ? " SSE" : ""),
383 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE2) ? " SSE2" : ""),
384 ((Ke386CpuidFlags2 & X86_EXT_FEATURE_SSE3) ? " SSE3" : ""));
385 }
386 if (Ke386GetCr4() & X86_CR4_OSFXSR)
387 {
388 DPRINT("SSE enabled.\n");
389 }
390 if (Ke386GetCr4() & X86_CR4_OSXMMEXCPT)
391 {
392 DPRINT("Unmasked SIMD exceptions enabled.\n");
393 }
394 if (Pcr->PrcbData.VendorString[0])
395 {
396 DPRINT("CPU Vendor: %s\n", Pcr->PrcbData.VendorString);
397 }
398 if (Ke386CpuidModel[0])
399 {
400 DPRINT("CPU Model: %s\n", Ke386CpuidModel);
401 }
402
403 DPRINT("Ke386CacheAlignment: %d\n", Ke386CacheAlignment);
404 if (Ke386L1CacheSize)
405 {
406 DPRINT("Ke386L1CacheSize: %dkB\n", Ke386L1CacheSize);
407 }
408 if (Pcr->L2CacheSize)
409 {
410 DPRINT("Ke386L2CacheSize: %dkB\n", Pcr->L2CacheSize);
411 }
412 }
413
414 VOID INIT_FUNCTION
415 Ki386SetProcessorFeatures(VOID)
416 {
417 PKPCR Pcr = KeGetCurrentKPCR();
418 OBJECT_ATTRIBUTES ObjectAttributes;
419 UNICODE_STRING KeyName;
420 UNICODE_STRING ValueName;
421 HANDLE KeyHandle;
422 ULONG ResultLength;
423 KEY_VALUE_PARTIAL_INFORMATION ValueData;
424 NTSTATUS Status;
425 ULONG FastSystemCallDisable = 0;
426
427 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
428 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = FALSE;
429 SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
430 (Pcr->PrcbData.FeatureBits & X86_FEATURE_CX8);
431 SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
432 (Pcr->PrcbData.FeatureBits & X86_FEATURE_MMX);
433 SharedUserData->ProcessorFeatures[PF_PPC_MOVEMEM_64BIT_OK] = FALSE;
434 SharedUserData->ProcessorFeatures[PF_ALPHA_BYTE_INSTRUCTIONS] = FALSE;
435 SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
436 (Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE);
437 SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
438 (Ke386CpuidExFlags & X86_EXT_FEATURE_3DNOW);
439 SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
440 (Pcr->PrcbData.FeatureBits & X86_FEATURE_TSC);
441 SharedUserData->ProcessorFeatures[PF_PAE_ENABLED] = Ke386Pae;
442 SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
443 (Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE2);
444
445 /* Does the CPU Support Fast System Call? */
446 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_SYSCALL) {
447
448 /* FIXME: Check for Family == 6, Model < 3 and Stepping < 3 and disable */
449
450 /* Make sure it's not disabled in registry */
451 RtlRosInitUnicodeStringFromLiteral(&KeyName,
452 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Kernel");
453 RtlRosInitUnicodeStringFromLiteral(&ValueName,
454 L"FastSystemCallDisable");
455 InitializeObjectAttributes(&ObjectAttributes,
456 &KeyName,
457 OBJ_CASE_INSENSITIVE,
458 NULL,
459 NULL);
460 Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
461
462 if (NT_SUCCESS(Status)) {
463
464 /* Read the Value then Close the Key */
465 Status = NtQueryValueKey(KeyHandle,
466 &ValueName,
467 KeyValuePartialInformation,
468 &ValueData,
469 sizeof(ValueData),
470 &ResultLength);
471 RtlMoveMemory(&FastSystemCallDisable, ValueData.Data, sizeof(ULONG));
472
473 NtClose(KeyHandle);
474 }
475
476 } else {
477
478 /* Disable SYSENTER/SYSEXIT, because the CPU doesn't support it */
479 FastSystemCallDisable = 1;
480
481 }
482
483 if (FastSystemCallDisable) {
484
485 /* Use INT2E */
486 SharedUserData->SystemCall[0] = 0x8D;
487 SharedUserData->SystemCall[1] = 0x54;
488 SharedUserData->SystemCall[2] = 0x24;
489 SharedUserData->SystemCall[3] = 0x08;
490 SharedUserData->SystemCall[4] = 0xCD;
491 SharedUserData->SystemCall[5] = 0x2E;
492 SharedUserData->SystemCall[6] = 0xC3;
493
494 } else {
495
496 /* Use SYSENTER */
497 SharedUserData->SystemCall[0] = 0x8B;
498 SharedUserData->SystemCall[1] = 0xD4;
499 SharedUserData->SystemCall[2] = 0x0F;
500 SharedUserData->SystemCall[3] = 0x34;
501 SharedUserData->SystemCall[4] = 0xC3;
502
503 /* Enable SYSENTER/SYSEXIT */
504 KiFastSystemCallDisable = 0;
505 }
506 }