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