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