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