ntoskrnl header cleanups
[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 DPRINT("KePrepareForApplicationProcessorInit(Id %d)\n", Id);
174 PFN_TYPE PrcPfn;
175 PKIPCR Pcr;
176 PKIPCR BootPcr;
177
178 BootPcr = (PKIPCR)KPCR_BASE;
179 Pcr = (PKIPCR)((ULONG_PTR)KPCR_BASE + Id * PAGE_SIZE);
180
181 MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &PrcPfn);
182 MmCreateVirtualMappingForKernel((PVOID)Pcr,
183 PAGE_READWRITE,
184 &PrcPfn,
185 1);
186 /*
187 * Create a PCR for this processor
188 */
189 memset(Pcr, 0, PAGE_SIZE);
190 Pcr->Number = Id;
191 Pcr->SetMember = 1 << Id;
192 Pcr->NtTib.Self = &Pcr->NtTib;
193 Pcr->Self = (PKPCR)Pcr;
194 Pcr->Prcb = &Pcr->PrcbData;
195 Pcr->Irql = SYNCH_LEVEL;
196
197 Pcr->PrcbData.SetMember = 1 << Id;
198 Pcr->PrcbData.MHz = BootPcr->PrcbData.MHz;
199 Pcr->StallScaleFactor = BootPcr->StallScaleFactor;
200
201 /* Mark the end of the exception handler list */
202 Pcr->NtTib.ExceptionList = (PVOID)-1;
203
204 KiGdtPrepareForApplicationProcessorInit(Id);
205
206 KeActiveProcessors |= 1 << Id;
207 }
208
209 VOID
210 NTAPI
211 KeApplicationProcessorInit(VOID)
212 {
213 ULONG Offset;
214 PKIPCR Pcr;
215
216 DPRINT("KeApplicationProcessorInit()\n");
217
218 if (Ke386GlobalPagesEnabled)
219 {
220 /* Enable global pages */
221 Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
222 }
223
224
225 Offset = InterlockedIncrementUL(&PcrsAllocated) - 1;
226 Pcr = (PKIPCR)((ULONG_PTR)KPCR_BASE + Offset * PAGE_SIZE);
227
228 /*
229 * Initialize the GDT
230 */
231 KiInitializeGdt((PKPCR)Pcr);
232
233 /* Get processor information. */
234 Ki386GetCpuId();
235
236 /* Check FPU/MMX/SSE support. */
237 KiCheckFPU();
238
239 KeInitDpc(Pcr->Prcb);
240
241 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_SYSCALL)
242 {
243 extern void KiFastCallEntry(void);
244
245 /* CS Selector of the target segment. */
246 Ke386Wrmsr(0x174, KERNEL_CS, 0);
247 /* Target ESP. */
248 Ke386Wrmsr(0x175, 0, 0);
249 /* Target EIP. */
250 Ke386Wrmsr(0x176, (ULONG_PTR)KiFastCallEntry, 0);
251 }
252
253 /*
254 * It is now safe to process interrupts
255 */
256 KeLowerIrql(DISPATCH_LEVEL);
257
258 /*
259 * Initialize the TSS
260 */
261 Ki386ApplicationProcessorInitializeTSS();
262
263 /*
264 * Initialize a default LDT
265 */
266 Ki386InitializeLdt();
267
268 /* Now we can enable interrupts. */
269 Ke386EnableInterrupts();
270 }
271
272 VOID
273 INIT_FUNCTION
274 NTAPI
275 KeInit1(PCHAR CommandLine, PULONG LastKernelAddress)
276 {
277 PKIPCR KPCR;
278 BOOLEAN Pae = FALSE;
279 BOOLEAN NoExecute = FALSE;
280 PCHAR p1, p2;
281 extern USHORT KiBootGdt[];
282 extern KTSS KiBootTss;
283
284 /*
285 * Initialize the initial PCR region. We can't allocate a page
286 * with MmAllocPage() here because MmInit1() has not yet been
287 * called, so we use a predefined page in low memory
288 */
289
290 KPCR = (PKIPCR)KPCR_BASE;
291 memset(KPCR, 0, PAGE_SIZE);
292 KPCR->Self = (PKPCR)KPCR;
293 KPCR->Prcb = &KPCR->PrcbData;
294 KPCR->Irql = SYNCH_LEVEL;
295 KPCR->NtTib.Self = &KPCR->NtTib;
296 KPCR->GDT = KiBootGdt;
297 KPCR->IDT = (PUSHORT)KiIdt;
298 KPCR->TSS = &KiBootTss;
299 KPCR->Number = 0;
300 KPCR->SetMember = 1 << 0;
301 KPCR->PrcbData.SetMember = 1 << 0;
302 KiPcrInitDone = 1;
303 PcrsAllocated++;
304
305 KiInitializeGdt (NULL);
306 Ki386BootInitializeTSS();
307 Ki386InitializeLdt();
308
309 /* Get processor information. */
310 Ki386GetCpuId();
311
312 /* Check FPU/MMX/SSE support. */
313 KiCheckFPU();
314
315 /* Mark the end of the exception handler list */
316 KPCR->NtTib.ExceptionList = (PVOID)-1;
317
318 KeInitDpc(KPCR->Prcb);
319
320 KeInitExceptions ();
321 KeInitInterrupts ();
322
323 KeActiveProcessors |= 1 << 0;
324
325
326 if (KPCR->PrcbData.FeatureBits & X86_FEATURE_PGE)
327 {
328 ULONG Flags;
329 /* Enable global pages */
330 Ke386GlobalPagesEnabled = TRUE;
331 Ke386SaveFlags(Flags);
332 Ke386DisableInterrupts();
333 Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
334 Ke386RestoreFlags(Flags);
335 }
336
337 /* Search for pae and noexecute */
338 p1 = (PCHAR)KeLoaderBlock.CommandLine;
339 while(*p1 && (p2 = strchr(p1, '/')))
340 {
341 p2++;
342 if (!_strnicmp(p2, "PAE", 3))
343 {
344 if (p2[3] == ' ' || p2[3] == 0)
345 {
346 p2 += 3;
347 Pae = TRUE;
348 }
349 }
350 else if (!_strnicmp(p2, "NOEXECUTE", 9))
351 {
352 if (p2[9] == ' ' || p2[9] == '=' || p2[9] == 0)
353 {
354 p2 += 9;
355 NoExecute = TRUE;
356 }
357 }
358 p1 = p2;
359 }
360 #if 0
361 /*
362 * FIXME:
363 * Make the detection of the noexecute feature more portable.
364 */
365 if(KPCR->PrcbData.CpuType == 0xf &&
366 RtlCompareMemory("AuthenticAMD", KPCR->PrcbData.VendorString, 12) == 12)
367 {
368 if (NoExecute)
369 {
370 ULONG Flags, l, h;
371 Ke386SaveFlags(Flags);
372 Ke386DisableInterrupts();
373
374 Ke386Rdmsr(0xc0000080, l, h);
375 l |= (1 << 11);
376 Ke386Wrmsr(0xc0000080, l, h);
377 Ke386NoExecute = TRUE;
378 Ke386RestoreFlags(Flags);
379 }
380 }
381 else
382 {
383 NoExecute=FALSE;
384 }
385 #endif
386
387 Ke386Pae = Ke386GetCr4() & X86_CR4_PAE ? TRUE : FALSE;
388 #if 0
389 /* Enable PAE mode */
390 if ((Pae && (KPCR->PrcbData.FeatureBits & X86_FEATURE_PAE)) || NoExecute)
391 {
392 MiEnablePAE((PVOID*)LastKernelAddress);
393 Ke386PaeEnabled = TRUE;
394 }
395 #endif
396 if (KPCR->PrcbData.FeatureBits & X86_FEATURE_SYSCALL)
397 {
398 extern void KiFastCallEntry(void);
399
400 /* CS Selector of the target segment. */
401 Ke386Wrmsr(0x174, KERNEL_CS, 0);
402 /* Target ESP. */
403 Ke386Wrmsr(0x175, 0, 0);
404 /* Target EIP. */
405 Ke386Wrmsr(0x176, (ULONG_PTR)KiFastCallEntry, 0);
406 }
407 }
408
409 VOID
410 INIT_FUNCTION
411 NTAPI
412 KeInit2(VOID)
413 {
414 PKIPCR Pcr = (PKIPCR)KeGetCurrentKPCR();
415
416 KiInitializeBugCheck();
417 KeInitializeDispatcher();
418 KiInitializeSystemClock();
419
420 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_PAE)
421 {
422 DPRINT("CPU supports PAE mode\n");
423 if (Ke386Pae)
424 {
425 DPRINT("CPU runs in PAE mode\n");
426 if (Ke386NoExecute)
427 {
428 DPRINT("NoExecute is enabled\n");
429 }
430 }
431 else
432 {
433 DPRINT("CPU doesn't run in PAE mode\n");
434 }
435 }
436 if ((Pcr->PrcbData.FeatureBits & (X86_FEATURE_FXSR | X86_FEATURE_MMX | X86_FEATURE_SSE | X86_FEATURE_SSE2)) ||
437 (Ke386CpuidFlags2 & X86_EXT_FEATURE_SSE3))
438 {
439 DPRINT("CPU supports" "%s%s%s%s%s" ".\n",
440 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_FXSR) ? " FXSR" : ""),
441 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_MMX) ? " MMX" : ""),
442 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE) ? " SSE" : ""),
443 ((Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE2) ? " SSE2" : ""),
444 ((Ke386CpuidFlags2 & X86_EXT_FEATURE_SSE3) ? " SSE3" : ""));
445 }
446 if (Ke386GetCr4() & X86_CR4_OSFXSR)
447 {
448 DPRINT("SSE enabled.\n");
449 }
450 if (Ke386GetCr4() & X86_CR4_OSXMMEXCPT)
451 {
452 DPRINT("Unmasked SIMD exceptions enabled.\n");
453 }
454 if (Pcr->PrcbData.VendorString[0])
455 {
456 DPRINT("CPU Vendor: %s\n", Pcr->PrcbData.VendorString);
457 }
458 if (Ke386CpuidModel[0])
459 {
460 DPRINT("CPU Model: %s\n", Ke386CpuidModel);
461 }
462
463 DPRINT("Ke386CacheAlignment: %d\n", Ke386CacheAlignment);
464 if (Ke386L1CacheSize)
465 {
466 DPRINT("Ke386L1CacheSize: %dkB\n", Ke386L1CacheSize);
467 }
468 if (Pcr->L2CacheSize)
469 {
470 DPRINT("Ke386L2CacheSize: %dkB\n", Pcr->L2CacheSize);
471 }
472 }
473
474 VOID INIT_FUNCTION
475 Ki386SetProcessorFeatures(VOID)
476 {
477 PKIPCR Pcr = (PKIPCR)KeGetCurrentKPCR();
478 OBJECT_ATTRIBUTES ObjectAttributes;
479 UNICODE_STRING KeyName =
480 RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Kernel");
481 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"FastSystemCallDisable");
482 HANDLE KeyHandle;
483 ULONG ResultLength;
484 KEY_VALUE_PARTIAL_INFORMATION ValueData;
485 NTSTATUS Status;
486 ULONG FastSystemCallDisable = 0;
487
488 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
489 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = FALSE;
490 SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
491 (Pcr->PrcbData.FeatureBits & X86_FEATURE_CX8);
492 SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
493 (Pcr->PrcbData.FeatureBits & X86_FEATURE_MMX);
494 SharedUserData->ProcessorFeatures[PF_PPC_MOVEMEM_64BIT_OK] = FALSE;
495 SharedUserData->ProcessorFeatures[PF_ALPHA_BYTE_INSTRUCTIONS] = FALSE;
496 SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
497 (Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE);
498 SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
499 (Ke386CpuidExFlags & X86_EXT_FEATURE_3DNOW);
500 SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
501 (Pcr->PrcbData.FeatureBits & X86_FEATURE_TSC);
502 SharedUserData->ProcessorFeatures[PF_PAE_ENABLED] = Ke386Pae;
503 SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
504 (Pcr->PrcbData.FeatureBits & X86_FEATURE_SSE2);
505
506 /* Does the CPU Support Fast System Call? */
507 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_SYSCALL) {
508
509 /* FIXME: Check for Family == 6, Model < 3 and Stepping < 3 and disable */
510
511 /* Make sure it's not disabled in registry */
512 InitializeObjectAttributes(&ObjectAttributes,
513 &KeyName,
514 OBJ_CASE_INSENSITIVE,
515 NULL,
516 NULL);
517 Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
518
519 if (NT_SUCCESS(Status)) {
520
521 /* Read the Value then Close the Key */
522 Status = NtQueryValueKey(KeyHandle,
523 &ValueName,
524 KeyValuePartialInformation,
525 &ValueData,
526 sizeof(ValueData),
527 &ResultLength);
528 RtlMoveMemory(&FastSystemCallDisable, ValueData.Data, sizeof(ULONG));
529
530 NtClose(KeyHandle);
531 }
532
533 } else {
534
535 /* Disable SYSENTER/SYSEXIT, because the CPU doesn't support it */
536 FastSystemCallDisable = 1;
537
538 }
539
540 if (FastSystemCallDisable) {
541 /* Use INT2E */
542 const unsigned char Entry[7] = {0x8D, 0x54, 0x24, 0x08, /* lea 0x8(%esp),%edx */
543 0xCD, 0x2E, /* int 0x2e */
544 0xC3}; /* ret */
545 memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));
546 } else {
547 /* Use SYSENTER */
548 const unsigned char Entry[5] = {0x8B, 0xD4, /* movl %esp,%edx */
549 0x0F, 0x34, /* sysenter */
550 0xC3}; /* ret */
551 memcpy(&SharedUserData->SystemCall, Entry, sizeof(Entry));
552 /* Enable SYSENTER/SYSEXIT */
553 KiFastSystemCallDisable = 0;
554 }
555 }