[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ke / amd64 / kiinit.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/kiinit.c
5 * PURPOSE: Kernel Initialization for x86 CPUs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Timo Kreuzer (timo.kreuzer@reactos.org)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define REQUIRED_FEATURE_BITS (KF_RDTSC|KF_CR4|KF_CMPXCHG8B|KF_XMMI|KF_XMMI64| \
17 KF_NX_BIT)
18
19 /* GLOBALS *******************************************************************/
20
21 /* Function pointer for early debug prints */
22 ULONG (*FrLdrDbgPrint)(const char *Format, ...);
23
24 /* Spinlocks used only on X86 */
25 KSPIN_LOCK KiFreezeExecutionLock;
26
27 /* BIOS Memory Map. Not NTLDR-compliant yet */
28 extern ULONG KeMemoryMapRangeCount;
29 extern ADDRESS_RANGE KeMemoryMap[64];
30
31 KIPCR KiInitialPcr;
32
33 /* Boot and double-fault/NMI/DPC stack */
34 UCHAR DECLSPEC_ALIGN(16) P0BootStackData[KERNEL_STACK_SIZE] = {0};
35 UCHAR DECLSPEC_ALIGN(16) KiDoubleFaultStackData[KERNEL_STACK_SIZE] = {0};
36 ULONG_PTR P0BootStack = (ULONG_PTR)&P0BootStackData[KERNEL_STACK_SIZE];
37 ULONG_PTR KiDoubleFaultStack = (ULONG_PTR)&KiDoubleFaultStackData[KERNEL_STACK_SIZE];
38
39 /* FUNCTIONS *****************************************************************/
40
41 VOID
42 NTAPI
43 KiInitMachineDependent(VOID)
44 {
45 /* Check for large page support */
46 if (KeFeatureBits & KF_LARGE_PAGE)
47 {
48 /* FIXME: Support this */
49 DPRINT1("Large Page support detected but not yet taken advantage of!\n");
50 }
51
52 /* Check for global page support */
53 if (KeFeatureBits & KF_GLOBAL_PAGE)
54 {
55 /* FIXME: Support this */
56 DPRINT1("Global Page support detected but not yet taken advantage of!\n");
57 }
58
59 /* Check if we have MTRR */
60 if (KeFeatureBits & KF_MTRR)
61 {
62 /* FIXME: Support this */
63 DPRINT1("MTRR support detected but not yet taken advantage of!\n");
64 }
65
66 /* Check for PAT and/or MTRR support */
67 if (KeFeatureBits & KF_PAT)
68 {
69 /* FIXME: Support this */
70 DPRINT1("PAT support detected but not yet taken advantage of!\n");
71 }
72
73
74 }
75
76 VOID
77 NTAPI
78 KiInitializePcr(IN PKIPCR Pcr,
79 IN ULONG ProcessorNumber,
80 IN PKTHREAD IdleThread,
81 IN PVOID DpcStack)
82 {
83 KDESCRIPTOR GdtDescriptor = {{0},0,0}, IdtDescriptor = {{0},0,0};
84 PKGDTENTRY64 TssEntry;
85 USHORT Tr = 0;
86
87 /* Zero out the PCR */
88 RtlZeroMemory(Pcr, PAGE_SIZE);
89
90 /* Set pointers to ourselves */
91 Pcr->Self = (PKPCR)Pcr;
92 Pcr->CurrentPrcb = &Pcr->Prcb;
93
94 /* Set the PCR Version */
95 Pcr->MajorVersion = PCR_MAJOR_VERSION;
96 Pcr->MinorVersion = PCR_MINOR_VERSION;
97
98 /* Set the PRCB Version */
99 Pcr->Prcb.MajorVersion = 1;
100 Pcr->Prcb.MinorVersion = 1;
101
102 /* Set the Build Type */
103 Pcr->Prcb.BuildType = 0;
104 #ifndef CONFIG_SMP
105 Pcr->Prcb.BuildType |= PRCB_BUILD_UNIPROCESSOR;
106 #endif
107 #ifdef DBG
108 Pcr->Prcb.BuildType |= PRCB_BUILD_DEBUG;
109 #endif
110
111 /* Set the Processor Number and current Processor Mask */
112 Pcr->Prcb.Number = (UCHAR)ProcessorNumber;
113 Pcr->Prcb.SetMember = 1 << ProcessorNumber;
114
115 /* Get GDT and IDT descriptors */
116 __sgdt(&GdtDescriptor.Limit);
117 __sidt(&IdtDescriptor.Limit);
118 Pcr->GdtBase = (PVOID)GdtDescriptor.Base;
119 Pcr->IdtBase = (PKIDTENTRY)IdtDescriptor.Base;
120
121 /* Get TSS Selector */
122 __str(&Tr);
123 ASSERT(Tr == KGDT64_SYS_TSS);
124
125 /* Get TSS Entry */
126 TssEntry = KiGetGdtEntry(Pcr->GdtBase, Tr);
127
128 /* Get the KTSS itself */
129 Pcr->TssBase = KiGetGdtDescriptorBase(TssEntry);
130
131 Pcr->Prcb.RspBase = Pcr->TssBase->Rsp0; // FIXME
132
133 /* Set DPC Stack */
134 Pcr->Prcb.DpcStack = DpcStack;
135
136 /* Setup the processor set */
137 Pcr->Prcb.MultiThreadProcessorSet = Pcr->Prcb.SetMember;
138
139 /* Clear DR6/7 to cleanup bootloader debugging */
140 Pcr->Prcb.ProcessorState.SpecialRegisters.KernelDr6 = 0;
141 Pcr->Prcb.ProcessorState.SpecialRegisters.KernelDr7 = 0;
142
143 /* Set the Current Thread */
144 Pcr->Prcb.CurrentThread = IdleThread;
145
146 /* Start us out at PASSIVE_LEVEL */
147 Pcr->Irql = PASSIVE_LEVEL;
148 KeSetCurrentIrql(PASSIVE_LEVEL);
149
150 }
151
152 VOID
153 NTAPI
154 KiInitializeCpuFeatures(ULONG Cpu)
155 {
156 ULONG FeatureBits;
157
158 /* Get the processor features for this CPU */
159 FeatureBits = KiGetFeatureBits();
160
161 /* Check if we support all needed features */
162 if ((FeatureBits & REQUIRED_FEATURE_BITS) != REQUIRED_FEATURE_BITS)
163 {
164 /* If not, bugcheck system */
165 FrLdrDbgPrint("CPU doesn't have needed features! Has: 0x%x, required: 0x%x\n",
166 FeatureBits, REQUIRED_FEATURE_BITS);
167 KeBugCheck(0);
168 }
169
170 /* Set DEP to always on */
171 SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSON;
172 FeatureBits |= KF_NX_ENABLED;
173
174 /* Save feature bits */
175 KeGetCurrentPrcb()->FeatureBits = FeatureBits;
176
177 /* Enable fx save restore support */
178 __writecr4(__readcr4() | CR4_FXSR);
179
180 /* Enable XMMI exceptions */
181 __writecr4(__readcr4() | CR4_XMMEXCPT);
182
183 /* Enable Write-Protection */
184 __writecr0(__readcr0() | CR0_WP);
185
186 /* Disable fpu monitoring */
187 __writecr0(__readcr0() & ~CR0_MP);
188
189 /* Disable x87 fpu exceptions */
190 __writecr0(__readcr0() & ~CR0_NE);
191
192 }
193
194 VOID
195 NTAPI
196 KiInitializeKernel(IN PKPROCESS InitProcess,
197 IN PKTHREAD InitThread,
198 IN PVOID IdleStack,
199 IN PKPRCB Prcb,
200 IN CCHAR Number,
201 IN PLOADER_PARAMETER_BLOCK LoaderBlock)
202 {
203 ULONG PageDirectory[2];
204 PVOID DpcStack;
205
206 /* Detect and set the CPU Type */
207 KiSetProcessorType();
208
209 /* Initialize the Power Management Support for this PRCB */
210 // PoInitializePrcb(Prcb);
211
212 /* Save CPU state */
213 KiSaveProcessorControlState(&Prcb->ProcessorState);
214
215 /* Get cache line information for this CPU */
216 KiGetCacheInformation();
217
218 /* Initialize spinlocks and DPC data */
219 KiInitSpinLocks(Prcb, Number);
220
221 /* Check if this is the Boot CPU */
222 if (Number == 0)
223 {
224 /* Set Node Data */
225 KeNodeBlock[0] = &KiNode0;
226 Prcb->ParentNode = KeNodeBlock[0];
227 KeNodeBlock[0]->ProcessorMask = Prcb->SetMember;
228
229 /* Set boot-level flags */
230 KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
231 KeProcessorLevel = (USHORT)Prcb->CpuType;
232 if (Prcb->CpuID) KeProcessorRevision = Prcb->CpuStep;
233
234 /* Set the current MP Master KPRCB to the Boot PRCB */
235 Prcb->MultiThreadSetMaster = Prcb;
236
237 /* Lower to APC_LEVEL */
238 KeLowerIrql(APC_LEVEL);
239
240 /* Initialize some spinlocks */
241 KeInitializeSpinLock(&KiFreezeExecutionLock);
242
243 /* Initialize portable parts of the OS */
244 KiInitSystem();
245
246 /* Initialize the Idle Process and the Process Listhead */
247 InitializeListHead(&KiProcessListHead);
248 PageDirectory[0] = 0;
249 PageDirectory[1] = 0;
250 KeInitializeProcess(InitProcess,
251 0,
252 0xFFFFFFFF,
253 PageDirectory,
254 FALSE);
255 InitProcess->QuantumReset = MAXCHAR;
256 }
257 else
258 {
259 /* FIXME */
260 DPRINT1("SMP Boot support not yet present\n");
261 }
262
263 /* HACK for MmUpdatePageDir */
264 ((PETHREAD)InitThread)->ThreadsProcess = (PEPROCESS)InitProcess;
265
266 /* Setup the Idle Thread */
267 KeInitializeThread(InitProcess,
268 InitThread,
269 NULL,
270 NULL,
271 NULL,
272 NULL,
273 NULL,
274 IdleStack);
275
276 InitThread->NextProcessor = Number;
277 InitThread->Priority = HIGH_PRIORITY;
278 InitThread->State = Running;
279 InitThread->Affinity = 1 << Number;
280 InitThread->WaitIrql = DISPATCH_LEVEL;
281 InitProcess->ActiveProcessors = 1 << Number;
282
283 /* Set basic CPU Features that user mode can read */
284 SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
285 (KeFeatureBits & KF_MMX) ? TRUE: FALSE;
286 SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
287 (KeFeatureBits & KF_CMPXCHG8B) ? TRUE: FALSE;
288 SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
289 ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI)) ? TRUE: FALSE;
290 SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
291 ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI64)) ? TRUE: FALSE;
292 SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
293 (KeFeatureBits & KF_3DNOW) ? TRUE: FALSE;
294 SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
295 (KeFeatureBits & KF_RDTSC) ? TRUE: FALSE;
296
297 /* Set up the thread-related fields in the PRCB */
298 Prcb->CurrentThread = InitThread;
299 Prcb->NextThread = NULL;
300 Prcb->IdleThread = InitThread;
301
302 /* Initialize the Kernel Executive */
303 ExpInitializeExecutive(Number, LoaderBlock);
304
305 /* Only do this on the boot CPU */
306 if (Number == 0)
307 {
308 /* Calculate the time reciprocal */
309 KiTimeIncrementReciprocal =
310 KiComputeReciprocal(KeMaximumIncrement,
311 &KiTimeIncrementShiftCount);
312
313 /* Update DPC Values in case they got updated by the executive */
314 Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
315 Prcb->MinimumDpcRate = KiMinimumDpcRate;
316 Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
317
318 /* Allocate the DPC Stack */
319 DpcStack = MmCreateKernelStack(FALSE, 0);
320 if (!DpcStack) KeBugCheckEx(NO_PAGES_AVAILABLE, 1, 0, 0, 0);
321 Prcb->DpcStack = DpcStack;
322
323 /* Allocate the IOPM save area. */
324 // Ki386IopmSaveArea = ExAllocatePoolWithTag(PagedPool,
325 // PAGE_SIZE * 2,
326 // TAG('K', 'e', ' ', ' '));
327 // if (!Ki386IopmSaveArea)
328 // {
329 // /* Bugcheck. We need this for V86/VDM support. */
330 // KeBugCheckEx(NO_PAGES_AVAILABLE, 2, PAGE_SIZE * 2, 0, 0);
331 // }
332 }
333
334 /* Raise to Dispatch */
335 KfRaiseIrql(DISPATCH_LEVEL);
336
337 /* Set the Idle Priority to 0. This will jump into Phase 1 */
338 KeSetPriorityThread(InitThread, 0);
339
340 /* If there's no thread scheduled, put this CPU in the Idle summary */
341 KiAcquirePrcbLock(Prcb);
342 if (!Prcb->NextThread) KiIdleSummary |= 1 << Number;
343 KiReleasePrcbLock(Prcb);
344
345 /* Raise back to HIGH_LEVEL and clear the PRCB for the loader block */
346 KfRaiseIrql(HIGH_LEVEL);
347 LoaderBlock->Prcb = 0;
348 }
349
350 VOID
351 NTAPI
352 KiSystemStartup(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
353 {
354 ULONG Cpu;
355 PKTHREAD InitialThread;
356 ULONG64 InitialStack;
357 PKIPCR Pcr;
358
359 /* HACK */
360 FrLdrDbgPrint = LoaderBlock->u.I386.CommonDataArea;
361 FrLdrDbgPrint("Hello from KiSystemStartup!!!\n");
362
363 /* HACK, because freeldr maps page 0 */
364 MiAddressToPte((PVOID)0)->u.Hard.Valid = 0;
365
366 /* Save the loader block */
367 KeLoaderBlock = LoaderBlock;
368
369 /* Get the current CPU number */
370 Cpu = KeNumberProcessors++; // FIXME
371
372 /* LoaderBlock initialization for Cpu 0 */
373 if (Cpu == 0)
374 {
375 /* Set the initial stack, idle thread and process */
376 LoaderBlock->KernelStack = (ULONG_PTR)P0BootStack;
377 LoaderBlock->Thread = (ULONG_PTR)&KiInitialThread;
378 LoaderBlock->Process = (ULONG_PTR)&KiInitialProcess.Pcb;
379 LoaderBlock->Prcb = (ULONG_PTR)&KiInitialPcr.Prcb;
380 }
381
382 /* Get Pcr from loader block */
383 Pcr = CONTAINING_RECORD(LoaderBlock->Prcb, KIPCR, Prcb);
384
385 /* Set the PRCB for this Processor */
386 KiProcessorBlock[Cpu] = &Pcr->Prcb;
387
388 /* Set GS base */
389 __writemsr(X86_MSR_GSBASE, (ULONG64)Pcr);
390 __writemsr(X86_MSR_KERNEL_GSBASE, (ULONG64)Pcr);
391
392 /* LDT is unused */
393 __lldt(0);
394
395 /* Align stack to 16 bytes */
396 LoaderBlock->KernelStack &= ~(16 - 1);
397
398 /* Save the initial thread and stack */
399 InitialStack = LoaderBlock->KernelStack; // Checkme
400 InitialThread = (PKTHREAD)LoaderBlock->Thread;
401
402 /* Clean the APC List Head */
403 InitializeListHead(&InitialThread->ApcState.ApcListHead[KernelMode]);
404
405 /* Set us as the current process */
406 InitialThread->ApcState.Process = (PVOID)LoaderBlock->Process;
407
408 /* Initialize the PCR */
409 KiInitializePcr(Pcr, Cpu, InitialThread, (PVOID)KiDoubleFaultStack);
410
411 /* Initialize the CPU features */
412 KiInitializeCpuFeatures(Cpu);
413
414 /* Initial setup for the boot CPU */
415 if (Cpu == 0)
416 {
417 /* Setup the TSS descriptors and entries */
418 KiInitializeTss(Pcr->TssBase, InitialStack);
419
420 /* Setup the IDT */
421 KeInitExceptions();
422
423 /* HACK: misuse this function to pass a function pointer to kdcom */
424 KdDebuggerInitialize1((PVOID)FrLdrDbgPrint);
425
426 /* Initialize debugging system */
427 KdInitSystem(0, KeLoaderBlock);
428
429 /* Check for break-in */
430 if (KdPollBreakIn()) DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
431
432 /* Hack! Wait for the debugger! */
433 #ifdef _WINKD_
434 while (!KdPollBreakIn());
435 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
436 #endif
437 }
438
439 DPRINT("Pcr = %p, Gdt = %p, Idt = %p, Tss = %p\n",
440 Pcr, Pcr->GdtBase, Pcr->IdtBase, Pcr->TssBase);
441
442 /* Acquire lock */
443 while (InterlockedBitTestAndSet64((PLONG64)&KiFreezeExecutionLock, 0))
444 {
445 /* Loop until lock is free */
446 while ((*(volatile KSPIN_LOCK*)&KiFreezeExecutionLock) & 1);
447 }
448
449 /* Initialize the Processor with HAL */
450 HalInitializeProcessor(Cpu, KeLoaderBlock);
451
452 /* Set processor as active */
453 KeActiveProcessors |= 1 << Cpu;
454
455 /* Release lock */
456 InterlockedAnd64((PLONG64)&KiFreezeExecutionLock, 0);
457
458 /* Raise to HIGH_LEVEL */
459 KfRaiseIrql(HIGH_LEVEL);
460
461 /* Switch to new kernel stack and start kernel bootstrapping */
462 KiSetupStackAndInitializeKernel(&KiInitialProcess.Pcb,
463 InitialThread,
464 (PVOID)InitialStack,
465 &Pcr->Prcb,
466 (CCHAR)Cpu,
467 KeLoaderBlock);
468 }
469
470
471 VOID
472 NTAPI
473 KiInitializeKernelAndGotoIdleLoop(IN PKPROCESS InitProcess,
474 IN PKTHREAD InitThread,
475 IN PVOID IdleStack,
476 IN PKPRCB Prcb,
477 IN CCHAR Number,
478 IN PLOADER_PARAMETER_BLOCK LoaderBlock)
479 {
480 // DbgBreakPointWithStatus(0);
481
482 /* Initialize kernel */
483 KiInitializeKernel(InitProcess,
484 InitThread,
485 IdleStack,
486 Prcb,
487 Number,
488 KeLoaderBlock);
489
490 /* Set the priority of this thread to 0 */
491 InitThread->Priority = 0;
492
493 /* Force interrupts enabled and lower IRQL back to DISPATCH_LEVEL */
494 _enable();
495 KeLowerIrql(DISPATCH_LEVEL);
496
497 /* Set the right wait IRQL */
498 InitThread->WaitIrql = DISPATCH_LEVEL;
499
500 /* Jump into the idle loop */
501 KiIdleLoop();
502 }