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