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