- Initialize the pcr for an application processor before it is booted.
[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 ULONG Ke386CpuidFlags, Ke386CpuidFlags2, Ke386CpuidExFlags;
39 ULONG Ke386Cpuid = 0x300;
40 ULONG Ke386CacheAlignment;
41 CHAR Ke386CpuidVendor[13] = {0,};
42 CHAR Ke386CpuidModel[49] = {0,};
43 ULONG Ke386L1CacheSize;
44 ULONG Ke386L2CacheSize;
45 BOOLEAN Ke386NoExecute = FALSE;
46 BOOLEAN Ke386Pae = FALSE;
47 BOOLEAN Ke386PaeEnabled = FALSE;
48
49 /* FUNCTIONS *****************************************************************/
50
51 VOID INIT_FUNCTION STATIC
52 Ki386GetCpuId(VOID)
53 {
54 ULONG OrigFlags, Flags, FinalFlags;
55 ULONG MaxCpuidLevel;
56 ULONG Dummy, Ebx, Ecx, Edx;
57
58 Ke386CpuidFlags = Ke386CpuidFlags2 = Ke386CpuidExFlags = 0;
59 Ke386CacheAlignment = 32;
60
61 /* Try to toggle the id bit in eflags. */
62 __asm__ ("pushfl\n\t"
63 "popl %0\n\t"
64 : "=r" (OrigFlags));
65 Flags = OrigFlags ^ X86_EFLAGS_ID;
66 __asm__ ("pushl %1\n\t"
67 "popfl\n\t"
68 "pushfl\n\t"
69 "popl %0\n\t"
70 : "=r" (FinalFlags)
71 : "r" (Flags));
72 if ((OrigFlags & X86_EFLAGS_ID) == (FinalFlags & X86_EFLAGS_ID))
73 {
74 /* No cpuid supported. */
75 return;
76 }
77
78 /* Get the vendor name and the maximum cpuid level supported. */
79 Ki386Cpuid(0, &MaxCpuidLevel, (PULONG)&Ke386CpuidVendor[0], (PULONG)&Ke386CpuidVendor[8], (PULONG)&Ke386CpuidVendor[4]);
80 if (MaxCpuidLevel > 0)
81 {
82 /* Get the feature flags. */
83 Ki386Cpuid(1, &Ke386Cpuid, &Ebx, &Ke386CpuidFlags2, &Ke386CpuidFlags);
84 /* Get the cache alignment, if it is available */
85 if (Ke386CpuidFlags & (1<<19))
86 {
87 Ke386CacheAlignment = ((Ebx >> 8) & 0xff) * 8;
88 }
89 }
90
91 /* Get the maximum extended cpuid level supported. */
92 Ki386Cpuid(0x80000000, &MaxCpuidLevel, &Dummy, &Dummy, &Dummy);
93 if (MaxCpuidLevel > 0)
94 {
95 /* Get the extended feature flags. */
96 Ki386Cpuid(0x80000001, &Dummy, &Dummy, &Dummy, &Ke386CpuidExFlags);
97 }
98
99 /* Get the model name. */
100 if (MaxCpuidLevel >= 0x80000004)
101 {
102 PULONG v = (PULONG)&Ke386CpuidModel;
103 Ki386Cpuid(0x80000002, v, v + 1, v + 2, v + 3);
104 Ki386Cpuid(0x80000003, v + 4, v + 5, v + 6, v + 7);
105 Ki386Cpuid(0x80000004, v + 8, v + 9, v + 10, v + 11);
106 }
107
108 /* Get the L1 cache size */
109 if (MaxCpuidLevel >= 0x80000005)
110 {
111 Ki386Cpuid(0x80000005, &Dummy, &Dummy, &Ecx, &Edx);
112 Ke386L1CacheSize = (Ecx >> 24)+(Edx >> 24);
113 if ((Ecx & 0xff) > 0)
114 {
115 Ke386CacheAlignment = Ecx & 0xff;
116 }
117 }
118
119 /* Get the L2 cache size */
120 if (MaxCpuidLevel >= 0x80000006)
121 {
122 Ki386Cpuid(0x80000006, &Dummy, &Dummy, &Ecx, &Dummy);
123 Ke386L2CacheSize = Ecx >> 16;
124 }
125 }
126
127 VOID INIT_FUNCTION
128 KePrepareForApplicationProcessorInit(ULONG Id)
129 {
130 DPRINT("KePrepareForApplicationProcessorInit(Id %d)\n", Id);
131 PFN_TYPE PrcPfn;
132 PKPCR Pcr;
133
134 Pcr = (PKPCR)((ULONG_PTR)KPCR_BASE + Id * PAGE_SIZE);
135
136 MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &PrcPfn);
137 MmCreateVirtualMappingForKernel((PVOID)Pcr,
138 PAGE_READWRITE,
139 &PrcPfn,
140 1);
141 /*
142 * Create a PCR for this processor
143 */
144 memset(Pcr, 0, PAGE_SIZE);
145 Pcr->ProcessorNumber = Id;
146 Pcr->Tib.Self = &Pcr->Tib;
147 Pcr->Self = Pcr;
148 Pcr->Irql = HIGH_LEVEL;
149
150 /* Mark the end of the exception handler list */
151 Pcr->Tib.ExceptionList = (PVOID)-1;
152
153 KeInitDpc(Pcr);
154
155
156 KiGdtPrepareForApplicationProcessorInit(Id);
157 }
158
159 VOID
160 KeApplicationProcessorInit(VOID)
161 {
162 ULONG Offset;
163 PKPCR Pcr;
164
165 DPRINT("KeApplicationProcessorInit()\n");
166
167 if (Ke386CpuidFlags & X86_FEATURE_PGE)
168 {
169 /* Enable global pages */
170 Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
171 }
172
173 /* Enable PAE mode */
174 if (Ke386CpuidFlags & X86_FEATURE_PAE)
175 {
176 MiEnablePAE(NULL);
177 }
178
179 Offset = InterlockedIncrement(&PcrsAllocated) - 1;
180 Pcr = (PKPCR)((ULONG_PTR)KPCR_BASE + Offset * PAGE_SIZE);
181
182 /*
183 * Initialize the GDT
184 */
185 KiInitializeGdt(Pcr);
186
187
188 /*
189 * It is now safe to process interrupts
190 */
191 KeLowerIrql(DISPATCH_LEVEL);
192
193 /*
194 * Initialize the TSS
195 */
196 Ki386ApplicationProcessorInitializeTSS();
197
198 /*
199 * Initialize a default LDT
200 */
201 Ki386InitializeLdt();
202
203 /* Now we can enable interrupts. */
204 Ke386EnableInterrupts();
205 }
206
207 VOID INIT_FUNCTION
208 KeInit1(PCHAR CommandLine, PULONG LastKernelAddress)
209 {
210 PKPCR KPCR;
211 BOOLEAN Pae = FALSE;
212 BOOLEAN NoExecute = FALSE;
213 PCHAR p1, p2;
214 extern USHORT KiBootGdt[];
215 extern KTSS KiBootTss;
216
217 KiCheckFPU();
218
219 KiInitializeGdt (NULL);
220 Ki386BootInitializeTSS();
221 KeInitExceptions ();
222 KeInitInterrupts ();
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 KPCR = (PKPCR)KPCR_BASE;
230 memset(KPCR, 0, PAGE_SIZE);
231 KPCR->Self = KPCR;
232 KPCR->Irql = HIGH_LEVEL;
233 KPCR->Tib.Self = &KPCR->Tib;
234 KPCR->GDT = KiBootGdt;
235 KPCR->IDT = (PUSHORT)KiIdt;
236 KPCR->TSS = &KiBootTss;
237 KPCR->ProcessorNumber = 0;
238 KiPcrInitDone = 1;
239 PcrsAllocated++;
240
241 KeInitDpc(KPCR);
242
243 /* Mark the end of the exception handler list */
244 KPCR->Tib.ExceptionList = (PVOID)-1;
245
246 Ki386InitializeLdt();
247
248 /* Get processor information. */
249 Ki386GetCpuId();
250
251 if (Ke386CpuidFlags & X86_FEATURE_PGE)
252 {
253 ULONG Flags;
254 /* Enable global pages */
255 Ke386SaveFlags(Flags);
256 Ke386DisableInterrupts();
257 Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE);
258 Ke386RestoreFlags(Flags);
259 }
260
261 /* Search for pae and noexecute */
262 p1 = (PCHAR)KeLoaderBlock.CommandLine;
263 while(*p1 && (p2 = strchr(p1, '/')))
264 {
265 p2++;
266 if (!_strnicmp(p2, "PAE", 3))
267 {
268 if (p2[3] == ' ' || p2[3] == 0)
269 {
270 p2 += 3;
271 Pae = TRUE;
272 }
273 }
274 else if (!_strnicmp(p2, "NOEXECUTE", 9))
275 {
276 if (p2[9] == ' ' || p2[9] == '=' || p2[9] == 0)
277 {
278 p2 += 9;
279 NoExecute = TRUE;
280 }
281 }
282 p1 = p2;
283 }
284
285 /*
286 * FIXME:
287 * Make the detection of the noexecute feature more portable.
288 */
289 if(((Ke386Cpuid >> 8) & 0xf) == 0xf &&
290 0 == strcmp("AuthenticAMD", Ke386CpuidVendor))
291 {
292 if (NoExecute)
293 {
294 ULONG Flags, l, h;
295 Ke386SaveFlags(Flags);
296 Ke386DisableInterrupts();
297
298 Ke386Rdmsr(0xc0000080, l, h);
299 l |= (1 << 11);
300 Ke386Wrmsr(0xc0000080, l, h);
301 Ke386NoExecute = TRUE;
302 Ke386RestoreFlags(Flags);
303 }
304 }
305 else
306 {
307 NoExecute=FALSE;
308 }
309
310
311 /* Enable PAE mode */
312 if ((Pae && (Ke386CpuidFlags & X86_FEATURE_PAE)) || NoExecute)
313 {
314 MiEnablePAE((PVOID*)LastKernelAddress);
315 Ke386PaeEnabled = TRUE;
316 }
317 }
318
319 VOID INIT_FUNCTION
320 KeInit2(VOID)
321 {
322 KeInitializeBugCheck();
323 KeInitializeDispatcher();
324 KeInitializeTimerImpl();
325
326 if (Ke386CpuidFlags & X86_FEATURE_PAE)
327 {
328 DPRINT1("CPU supports PAE mode\n");
329 if (Ke386Pae)
330 {
331 DPRINT1("CPU runs in PAE mode\n");
332 if (Ke386NoExecute)
333 {
334 DPRINT1("NoExecute is enabled\n");
335 }
336 }
337 else
338 {
339 DPRINT1("CPU doesn't run in PAE mode\n");
340 }
341 }
342 if (Ke386CpuidVendor[0])
343 {
344 DPRINT1("CPU Vendor: %s\n", Ke386CpuidVendor);
345 }
346 if (Ke386CpuidModel[0])
347 {
348 DPRINT1("CPU Model: %s\n", Ke386CpuidModel);
349 }
350
351 DPRINT1("Ke386CacheAlignment: %d\n", Ke386CacheAlignment);
352 if (Ke386L1CacheSize)
353 {
354 DPRINT1("Ke386L1CacheSize: %dkB\n", Ke386L1CacheSize);
355 }
356 if (Ke386L2CacheSize)
357 {
358 DPRINT1("Ke386L2CacheSize: %dkB\n", Ke386L2CacheSize);
359 }
360 }
361
362 VOID INIT_FUNCTION
363 Ki386SetProcessorFeatures(VOID)
364 {
365 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
366 SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = FALSE;
367 SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
368 (Ke386CpuidFlags & X86_FEATURE_CX8);
369 SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
370 (Ke386CpuidFlags & X86_FEATURE_MMX);
371 SharedUserData->ProcessorFeatures[PF_PPC_MOVEMEM_64BIT_OK] = FALSE;
372 SharedUserData->ProcessorFeatures[PF_ALPHA_BYTE_INSTRUCTIONS] = FALSE;
373 SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
374 (Ke386CpuidFlags & X86_FEATURE_SSE);
375 SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
376 (Ke386CpuidExFlags & X86_EXT_FEATURE_3DNOW);
377 SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
378 (Ke386CpuidFlags & X86_FEATURE_TSC);
379 SharedUserData->ProcessorFeatures[PF_PAE_ENABLED] = Ke386PaeEnabled;
380 SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
381 (Ke386CpuidFlags & X86_FEATURE_SSE2);
382 }