xref: /reactos/ntoskrnl/ke/i386/kiinit.c (revision f59c58d8)
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  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 #include "internal/i386/trap_x.h"
15 
16 /* GLOBALS *******************************************************************/
17 
18 /* Boot and double-fault/NMI/DPC stack */
19 UCHAR DECLSPEC_ALIGN(PAGE_SIZE) P0BootStackData[KERNEL_STACK_SIZE] = {0};
20 UCHAR DECLSPEC_ALIGN(PAGE_SIZE) KiDoubleFaultStackData[KERNEL_STACK_SIZE] = {0};
21 ULONG_PTR P0BootStack = (ULONG_PTR)&P0BootStackData[KERNEL_STACK_SIZE];
22 ULONG_PTR KiDoubleFaultStack = (ULONG_PTR)&KiDoubleFaultStackData[KERNEL_STACK_SIZE];
23 
24 /* Spinlocks used only on X86 */
25 KSPIN_LOCK KiFreezeExecutionLock;
26 KSPIN_LOCK Ki486CompatibilityLock;
27 
28 /* Perf */
29 ULONG ProcessCount;
30 ULONGLONG BootCycles, BootCyclesEnd;
31 
32 /* FUNCTIONS *****************************************************************/
33 
34 CODE_SEG("INIT")
35 VOID
36 NTAPI
37 KiInitMachineDependent(VOID)
38 {
39     ULONG CpuCount;
40     BOOLEAN FbCaching = FALSE;
41     NTSTATUS Status;
42     ULONG ReturnLength;
43     ULONG i, Affinity, Sample = 0;
44     PFX_SAVE_AREA FxSaveArea;
45     ULONG MXCsrMask = 0xFFBF;
46     CPU_INFO CpuInfo;
47     KI_SAMPLE_MAP Samples[10];
48     PKI_SAMPLE_MAP CurrentSample = Samples;
49     LARGE_IDENTITY_MAP IdentityMap;
50 
51     /* Check for large page support */
52     if (KeFeatureBits & KF_LARGE_PAGE)
53     {
54         /* Do an IPI to enable it on all CPUs */
55         if (Ki386CreateIdentityMap(&IdentityMap, Ki386EnableCurrentLargePage, 2))
56             KeIpiGenericCall(Ki386EnableTargetLargePage, (ULONG_PTR)&IdentityMap);
57 
58         /* Free the pages allocated for identity map */
59         Ki386FreeIdentityMap(&IdentityMap);
60     }
61 
62     /* Check for global page support */
63     if (KeFeatureBits & KF_GLOBAL_PAGE)
64     {
65         /* Do an IPI to enable it on all CPUs */
66         CpuCount = KeNumberProcessors;
67         KeIpiGenericCall(Ki386EnableGlobalPage, (ULONG_PTR)&CpuCount);
68     }
69 
70     /* Check for PAT and/or MTRR support */
71     if (KeFeatureBits & (KF_PAT | KF_MTRR))
72     {
73         /* Query the HAL to make sure we can use it */
74         Status = HalQuerySystemInformation(HalFrameBufferCachingInformation,
75                                            sizeof(BOOLEAN),
76                                            &FbCaching,
77                                            &ReturnLength);
78         if ((NT_SUCCESS(Status)) && (FbCaching))
79         {
80             /* We can't, disable it */
81             KeFeatureBits &= ~(KF_PAT | KF_MTRR);
82         }
83     }
84 
85     /* Check for PAT support and enable it */
86     if (KeFeatureBits & KF_PAT) KiInitializePAT();
87 
88     /* Check for CR4 support */
89     if (KeFeatureBits & KF_CR4)
90     {
91         /* Do an IPI call to enable the Debug Exceptions */
92         CpuCount = KeNumberProcessors;
93         KeIpiGenericCall(Ki386EnableDE, (ULONG_PTR)&CpuCount);
94     }
95 
96     /* Check if FXSR was found */
97     if (KeFeatureBits & KF_FXSR)
98     {
99         /* Do an IPI call to enable the FXSR */
100         CpuCount = KeNumberProcessors;
101         KeIpiGenericCall(Ki386EnableFxsr, (ULONG_PTR)&CpuCount);
102 
103         /* Check if XMM was found too */
104         if (KeFeatureBits & KF_XMMI)
105         {
106             /* Do an IPI call to enable XMMI exceptions */
107             CpuCount = KeNumberProcessors;
108             KeIpiGenericCall(Ki386EnableXMMIExceptions, (ULONG_PTR)&CpuCount);
109 
110             /* FIXME: Implement and enable XMM Page Zeroing for Mm */
111 
112             /* Patch the RtlPrefetchMemoryNonTemporal routine to enable it */
113             *(PCHAR)RtlPrefetchMemoryNonTemporal = 0x90; // NOP
114         }
115     }
116 
117     /* Check for, and enable SYSENTER support */
118     KiRestoreFastSyscallReturnState();
119 
120     /* Loop every CPU */
121     i = KeActiveProcessors;
122     for (Affinity = 1; i; Affinity <<= 1)
123     {
124         /* Check if this is part of the set */
125         if (i & Affinity)
126         {
127             /* Run on this CPU */
128             i &= ~Affinity;
129             KeSetSystemAffinityThread(Affinity);
130 
131             /* Reset MHz to 0 for this CPU */
132             KeGetCurrentPrcb()->MHz = 0;
133 
134             /* Check if we can use RDTSC */
135             if (KeFeatureBits & KF_RDTSC)
136             {
137                 /* Start sampling loop */
138                 for (;;)
139                 {
140                     /* Do a dummy CPUID to start the sample */
141                     KiCpuId(&CpuInfo, 0);
142 
143                     /* Fill out the starting data */
144                     CurrentSample->PerfStart = KeQueryPerformanceCounter(NULL);
145                     CurrentSample->TSCStart = __rdtsc();
146                     CurrentSample->PerfFreq.QuadPart = -50000;
147 
148                     /* Sleep for this sample */
149                     KeDelayExecutionThread(KernelMode,
150                                            FALSE,
151                                            &CurrentSample->PerfFreq);
152 
153                     /* Do another dummy CPUID */
154                     KiCpuId(&CpuInfo, 0);
155 
156                     /* Fill out the ending data */
157                     CurrentSample->PerfEnd =
158                         KeQueryPerformanceCounter(&CurrentSample->PerfFreq);
159                     CurrentSample->TSCEnd = __rdtsc();
160 
161                     /* Calculate the differences */
162                     CurrentSample->PerfDelta = CurrentSample->PerfEnd.QuadPart -
163                                                CurrentSample->PerfStart.QuadPart;
164                     CurrentSample->TSCDelta = CurrentSample->TSCEnd -
165                                               CurrentSample->TSCStart;
166 
167                     /* Compute CPU Speed */
168                     CurrentSample->MHz = (ULONG)((CurrentSample->TSCDelta *
169                                                   CurrentSample->
170                                                   PerfFreq.QuadPart + 500000) /
171                                                  (CurrentSample->PerfDelta *
172                                                   1000000));
173 
174                     /* Check if this isn't the first sample */
175                     if (Sample)
176                     {
177                         /* Check if we got a good precision within 1MHz */
178                         if ((CurrentSample->MHz == CurrentSample[-1].MHz) ||
179                             (CurrentSample->MHz == CurrentSample[-1].MHz + 1) ||
180                             (CurrentSample->MHz == CurrentSample[-1].MHz - 1))
181                         {
182                             /* We did, stop sampling */
183                             break;
184                         }
185                     }
186 
187                     /* Move on */
188                     CurrentSample++;
189                     Sample++;
190 
191                     if (Sample == RTL_NUMBER_OF(Samples))
192                     {
193                         /* No luck. Average the samples and be done */
194                         ULONG TotalMHz = 0;
195                         while (Sample--)
196                         {
197                             TotalMHz += Samples[Sample].MHz;
198                         }
199                         CurrentSample[-1].MHz = TotalMHz / RTL_NUMBER_OF(Samples);
200                         DPRINT1("Sampling CPU frequency failed. Using average of %lu MHz\n", CurrentSample[-1].MHz);
201                         break;
202                     }
203                 }
204 
205                 /* Save the CPU Speed */
206                 KeGetCurrentPrcb()->MHz = CurrentSample[-1].MHz;
207             }
208 
209             /* Check if we have MTRR */
210             if (KeFeatureBits & KF_MTRR)
211             {
212                 /* Then manually initialize MTRR for the CPU */
213                 KiInitializeMTRR(i ? FALSE : TRUE);
214             }
215 
216             /* Check if we have AMD MTRR and initialize it for the CPU */
217             if (KeFeatureBits & KF_AMDK6MTRR) KiAmdK6InitializeMTRR();
218 
219             /* Check if this is a buggy Pentium and apply the fixup if so */
220             if (KiI386PentiumLockErrataPresent) KiI386PentiumLockErrataFixup();
221 
222             /* Check if the CPU supports FXSR */
223             if (KeFeatureBits & KF_FXSR)
224             {
225                 /* Get the current thread NPX state */
226                 FxSaveArea = KiGetThreadNpxArea(KeGetCurrentThread());
227 
228                 /* Clear initial MXCsr mask */
229                 FxSaveArea->U.FxArea.MXCsrMask = 0;
230 
231                 /* Save the current NPX State */
232                 Ke386SaveFpuState(FxSaveArea);
233 
234                 /* Check if the current mask doesn't match the reserved bits */
235                 if (FxSaveArea->U.FxArea.MXCsrMask != 0)
236                 {
237                     /* Then use whatever it's holding */
238                     MXCsrMask = FxSaveArea->U.FxArea.MXCsrMask;
239                 }
240 
241                 /* Check if nobody set the kernel-wide mask */
242                 if (!KiMXCsrMask)
243                 {
244                     /* Then use the one we calculated above */
245                     KiMXCsrMask = MXCsrMask;
246                 }
247                 else
248                 {
249                     /* Was it set to the same value we found now? */
250                     if (KiMXCsrMask != MXCsrMask)
251                     {
252                         /* No, something is definitely wrong */
253                         KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED,
254                                      KF_FXSR,
255                                      KiMXCsrMask,
256                                      MXCsrMask,
257                                      0);
258                     }
259                 }
260 
261                 /* Now set the kernel mask */
262                 KiMXCsrMask &= MXCsrMask;
263             }
264         }
265     }
266 
267     /* Return affinity back to where it was */
268     KeRevertToUserAffinityThread();
269 
270     /* NT allows limiting the duration of an ISR with a registry key */
271     if (KiTimeLimitIsrMicroseconds)
272     {
273         /* FIXME: TODO */
274         DPRINT1("ISR Time Limit not yet supported\n");
275     }
276 
277     /* Set CR0 features based on detected CPU */
278     KiSetCR0Bits();
279 }
280 
281 CODE_SEG("INIT")
282 VOID
283 NTAPI
284 KiInitializePcr(IN ULONG ProcessorNumber,
285                 IN PKIPCR Pcr,
286                 IN PKIDTENTRY Idt,
287                 IN PKGDTENTRY Gdt,
288                 IN PKTSS Tss,
289                 IN PKTHREAD IdleThread,
290                 IN PVOID DpcStack)
291 {
292     /* Setup the TIB */
293     Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
294     Pcr->NtTib.StackBase = 0;
295     Pcr->NtTib.StackLimit = 0;
296     Pcr->NtTib.Self = NULL;
297 
298     /* Set the Current Thread */
299     Pcr->PrcbData.CurrentThread = IdleThread;
300 
301     /* Set pointers to ourselves */
302     Pcr->SelfPcr = (PKPCR)Pcr;
303     Pcr->Prcb = &Pcr->PrcbData;
304 
305     /* Set the PCR Version */
306     Pcr->MajorVersion = PCR_MAJOR_VERSION;
307     Pcr->MinorVersion = PCR_MINOR_VERSION;
308 
309     /* Set the PCRB Version */
310     Pcr->PrcbData.MajorVersion = 1;
311     Pcr->PrcbData.MinorVersion = 1;
312 
313     /* Set the Build Type */
314     Pcr->PrcbData.BuildType = 0;
315 #ifndef CONFIG_SMP
316     Pcr->PrcbData.BuildType |= PRCB_BUILD_UNIPROCESSOR;
317 #endif
318 #if DBG
319     Pcr->PrcbData.BuildType |= PRCB_BUILD_DEBUG;
320 #endif
321 
322     /* Set the Processor Number and current Processor Mask */
323     Pcr->PrcbData.Number = (UCHAR)ProcessorNumber;
324     Pcr->PrcbData.SetMember = 1 << ProcessorNumber;
325 
326     /* Set the PRCB for this Processor */
327     KiProcessorBlock[ProcessorNumber] = Pcr->Prcb;
328 
329     /* Start us out at PASSIVE_LEVEL */
330     Pcr->Irql = PASSIVE_LEVEL;
331 
332     /* Set the GDI, IDT, TSS and DPC Stack */
333     Pcr->GDT = (PVOID)Gdt;
334     Pcr->IDT = Idt;
335     Pcr->TSS = Tss;
336     Pcr->TssCopy = Tss;
337     Pcr->PrcbData.DpcStack = DpcStack;
338 
339     /* Setup the processor set */
340     Pcr->PrcbData.MultiThreadProcessorSet = Pcr->PrcbData.SetMember;
341 }
342 
343 static
344 CODE_SEG("INIT")
345 VOID
346 KiVerifyCpuFeatures(PKPRCB Prcb)
347 {
348     CPU_INFO CpuInfo;
349 
350     // 1. Check CPUID support
351     ULONG EFlags = __readeflags();
352 
353     /* XOR out the ID bit and update EFlags */
354     ULONG NewEFlags = EFlags ^ EFLAGS_ID;
355     __writeeflags(NewEFlags);
356 
357     /* Get them back and see if they were modified */
358     NewEFlags = __readeflags();
359 
360     if (NewEFlags == EFlags)
361     {
362         /* The modification did not work, so CPUID is not supported. */
363         KeBugCheckEx(UNSUPPORTED_PROCESSOR, 0x1, 0, 0, 0);
364     }
365     else
366     {
367         /* CPUID is supported. Set the ID Bit again. */
368         EFlags |= EFLAGS_ID;
369         __writeeflags(EFlags);
370     }
371 
372     /* Peform CPUID 0 to see if CPUID 1 is supported */
373     KiCpuId(&CpuInfo, 0);
374     if (CpuInfo.Eax == 0)
375     {
376         // 0x1 - missing CPUID instruction
377         KeBugCheckEx(UNSUPPORTED_PROCESSOR, 0x1, 0, 0, 0);
378     }
379 
380     // 2. Detect and set the CPU Type
381     KiSetProcessorType();
382 
383     if (Prcb->CpuType == 3)
384         KeBugCheckEx(UNSUPPORTED_PROCESSOR, 0x386, 0, 0, 0);
385 
386     // 3. Finally, obtain CPU features.
387     ULONG FeatureBits = KiGetFeatureBits();
388 
389     // 4. Verify it supports everything we need.
390     if (!(FeatureBits & KF_RDTSC))
391     {
392         // 0x2 - missing CPUID features
393         // second paramenter - edx flag which is missing
394         KeBugCheckEx(UNSUPPORTED_PROCESSOR, 0x2, 0x00000010, 0, 0);
395     }
396 
397     if (!(FeatureBits & KF_CMPXCHG8B))
398     {
399         KeBugCheckEx(UNSUPPORTED_PROCESSOR, 0x2, 0x00000100, 0, 0);
400     }
401 
402     // Check x87 FPU is present. FIXME: put this into FeatureBits?
403     KiCpuId(&CpuInfo, 1);
404 
405     if (!(CpuInfo.Edx & 0x00000001))
406     {
407         KeBugCheckEx(UNSUPPORTED_PROCESSOR, 0x2, 0x00000001, 0, 0);
408     }
409 
410     // Set up FPU-related CR0 flags.
411     ULONG Cr0 = __readcr0();
412     // Disable emulation and monitoring.
413     Cr0 &= ~(CR0_EM | CR0_MP);
414     // Enable FPU exceptions.
415     Cr0 |= CR0_NE;
416 
417     __writecr0(Cr0);
418 
419     // Check for Pentium FPU bug.
420     if (KiIsNpxErrataPresent())
421     {
422         KeBugCheckEx(UNSUPPORTED_PROCESSOR, 0x2, 0x00000001, 0, 0);
423     }
424 
425     // 5. Save feature bits.
426     Prcb->FeatureBits = FeatureBits;
427 }
428 
429 CODE_SEG("INIT")
430 VOID
431 NTAPI
432 KiInitializeKernel(IN PKPROCESS InitProcess,
433                    IN PKTHREAD InitThread,
434                    IN PVOID IdleStack,
435                    IN PKPRCB Prcb,
436                    IN CCHAR Number,
437                    IN PLOADER_PARAMETER_BLOCK LoaderBlock)
438 {
439     ULONG PageDirectory[2];
440     PVOID DpcStack;
441     KIRQL DummyIrql;
442 
443     /* Initialize the Power Management Support for this PRCB */
444     PoInitializePrcb(Prcb);
445 
446     /* Set boot-level flags */
447     if (Number == 0)
448         KeFeatureBits = Prcb->FeatureBits;
449 
450     /* Set the default NX policy (opt-in) */
451     SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_OPTIN;
452 
453     /* Check if NPX is always on */
454     if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=ALWAYSON"))
455     {
456         /* Set it always on */
457         SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSON;
458         KeFeatureBits |= KF_NX_ENABLED;
459     }
460     else if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=OPTOUT"))
461     {
462         /* Set it in opt-out mode */
463         SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_OPTOUT;
464         KeFeatureBits |= KF_NX_ENABLED;
465     }
466     else if ((strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=OPTIN")) ||
467              (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE")))
468     {
469         /* Set the feature bits */
470         KeFeatureBits |= KF_NX_ENABLED;
471     }
472     else if ((strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=ALWAYSOFF")) ||
473              (strstr(KeLoaderBlock->LoadOptions, "EXECUTE")))
474     {
475         /* Set disabled mode */
476         SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSOFF;
477         KeFeatureBits |= KF_NX_DISABLED;
478     }
479 
480     /* Save CPU state */
481     KiSaveProcessorControlState(&Prcb->ProcessorState);
482 
483     /* Get cache line information for this CPU */
484     KiGetCacheInformation();
485 
486     /* Initialize spinlocks and DPC data */
487     KiInitSpinLocks(Prcb, Number);
488 
489     /* Check if this is the Boot CPU */
490     if (!Number)
491     {
492         /* Set Node Data */
493         KeNodeBlock[0] = &KiNode0;
494         Prcb->ParentNode = KeNodeBlock[0];
495         KeNodeBlock[0]->ProcessorMask = Prcb->SetMember;
496 
497         /* Set boot-level flags */
498         KeI386CpuType = Prcb->CpuType;
499         KeI386CpuStep = Prcb->CpuStep;
500         KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
501         KeProcessorLevel = (USHORT)Prcb->CpuType;
502         if (Prcb->CpuID) KeProcessorRevision = Prcb->CpuStep;
503         KeI386FxsrPresent = (KeFeatureBits & KF_FXSR) ? TRUE : FALSE;
504         KeI386XMMIPresent = (KeFeatureBits & KF_XMMI) ? TRUE : FALSE;
505 
506         /* Set the current MP Master KPRCB to the Boot PRCB */
507         Prcb->MultiThreadSetMaster = Prcb;
508 
509         /* Lower to APC_LEVEL */
510         KeLowerIrql(APC_LEVEL);
511 
512         /* Initialize some spinlocks */
513         KeInitializeSpinLock(&KiFreezeExecutionLock);
514         KeInitializeSpinLock(&Ki486CompatibilityLock);
515 
516         /* Initialize portable parts of the OS */
517         KiInitSystem();
518 
519         /* Initialize the Idle Process and the Process Listhead */
520         InitializeListHead(&KiProcessListHead);
521         PageDirectory[0] = 0;
522         PageDirectory[1] = 0;
523         KeInitializeProcess(InitProcess,
524                             0,
525                             0xFFFFFFFF,
526                             PageDirectory,
527                             FALSE);
528         InitProcess->QuantumReset = MAXCHAR;
529     }
530     else
531     {
532         /* FIXME */
533         DPRINT1("SMP Boot support not yet present\n");
534     }
535 
536     /* Setup the Idle Thread */
537     KeInitializeThread(InitProcess,
538                        InitThread,
539                        NULL,
540                        NULL,
541                        NULL,
542                        NULL,
543                        NULL,
544                        IdleStack);
545     InitThread->NextProcessor = Number;
546     InitThread->Priority = HIGH_PRIORITY;
547     InitThread->State = Running;
548     InitThread->Affinity = 1 << Number;
549     InitThread->WaitIrql = DISPATCH_LEVEL;
550     InitProcess->ActiveProcessors = 1 << Number;
551 
552     /* HACK for MmUpdatePageDir */
553     ((PETHREAD)InitThread)->ThreadsProcess = (PEPROCESS)InitProcess;
554 
555     /* Set basic CPU Features that user mode can read */
556     SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
557     SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
558         (KeFeatureBits & KF_MMX) ? TRUE: FALSE;
559     SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
560         (KeFeatureBits & KF_CMPXCHG8B) ? TRUE: FALSE;
561     SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
562         ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI)) ? TRUE: FALSE;
563     SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
564         ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI64)) ? TRUE: FALSE;
565     SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
566         (KeFeatureBits & KF_3DNOW) ? TRUE: FALSE;
567     SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
568         (KeFeatureBits & KF_RDTSC) ? TRUE: FALSE;
569 
570     /* Set up the thread-related fields in the PRCB */
571     Prcb->CurrentThread = InitThread;
572     Prcb->NextThread = NULL;
573     Prcb->IdleThread = InitThread;
574 
575     /* Initialize the Kernel Executive */
576     ExpInitializeExecutive(Number, LoaderBlock);
577 
578     /* Only do this on the boot CPU */
579     if (!Number)
580     {
581         /* Calculate the time reciprocal */
582         KiTimeIncrementReciprocal =
583             KiComputeReciprocal(KeMaximumIncrement,
584                                 &KiTimeIncrementShiftCount);
585 
586         /* Update DPC Values in case they got updated by the executive */
587         Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
588         Prcb->MinimumDpcRate = KiMinimumDpcRate;
589         Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
590 
591         /* Allocate the DPC Stack */
592         DpcStack = MmCreateKernelStack(FALSE, 0);
593         if (!DpcStack) KeBugCheckEx(NO_PAGES_AVAILABLE, 1, 0, 0, 0);
594         Prcb->DpcStack = DpcStack;
595 
596         /* Allocate the IOPM save area */
597         Ki386IopmSaveArea = ExAllocatePoolWithTag(PagedPool,
598                                                   IOPM_SIZE,
599                                                   TAG_KERNEL);
600         if (!Ki386IopmSaveArea)
601         {
602             /* Bugcheck. We need this for V86/VDM support. */
603             KeBugCheckEx(NO_PAGES_AVAILABLE, 2, IOPM_SIZE, 0, 0);
604         }
605     }
606 
607     /* Raise to Dispatch */
608     KeRaiseIrql(DISPATCH_LEVEL, &DummyIrql);
609 
610     /* Set the Idle Priority to 0. This will jump into Phase 1 */
611     KeSetPriorityThread(InitThread, 0);
612 
613     /* If there's no thread scheduled, put this CPU in the Idle summary */
614     KiAcquirePrcbLock(Prcb);
615     if (!Prcb->NextThread) KiIdleSummary |= 1 << Number;
616     KiReleasePrcbLock(Prcb);
617 
618     /* Raise back to HIGH_LEVEL and clear the PRCB for the loader block */
619     KeRaiseIrql(HIGH_LEVEL, &DummyIrql);
620     LoaderBlock->Prcb = 0;
621 }
622 
623 CODE_SEG("INIT")
624 VOID
625 FASTCALL
626 KiGetMachineBootPointers(IN PKGDTENTRY *Gdt,
627                          IN PKIDTENTRY *Idt,
628                          IN PKIPCR *Pcr,
629                          IN PKTSS *Tss)
630 {
631     KDESCRIPTOR GdtDescriptor, IdtDescriptor;
632     KGDTENTRY TssSelector, PcrSelector;
633     USHORT Tr, Fs;
634 
635     /* Get GDT and IDT descriptors */
636     Ke386GetGlobalDescriptorTable(&GdtDescriptor.Limit);
637     __sidt(&IdtDescriptor.Limit);
638 
639     /* Save IDT and GDT */
640     *Gdt = (PKGDTENTRY)GdtDescriptor.Base;
641     *Idt = (PKIDTENTRY)IdtDescriptor.Base;
642 
643     /* Get TSS and FS Selectors */
644     Tr = Ke386GetTr();
645     Fs = Ke386GetFs();
646 
647     /* Get PCR Selector, mask it and get its GDT Entry */
648     PcrSelector = *(PKGDTENTRY)((ULONG_PTR)*Gdt + (Fs & ~RPL_MASK));
649 
650     /* Get the KPCR itself */
651     *Pcr = (PKIPCR)(ULONG_PTR)(PcrSelector.BaseLow |
652                                PcrSelector.HighWord.Bytes.BaseMid << 16 |
653                                PcrSelector.HighWord.Bytes.BaseHi << 24);
654 
655     /* Get TSS Selector, mask it and get its GDT Entry */
656     TssSelector = *(PKGDTENTRY)((ULONG_PTR)*Gdt + (Tr & ~RPL_MASK));
657 
658     /* Get the KTSS itself */
659     *Tss = (PKTSS)(ULONG_PTR)(TssSelector.BaseLow |
660                               TssSelector.HighWord.Bytes.BaseMid << 16 |
661                               TssSelector.HighWord.Bytes.BaseHi << 24);
662 }
663 
664 CODE_SEG("INIT")
665 DECLSPEC_NORETURN
666 VOID
667 NTAPI
668 KiSystemStartupBootStack(VOID)
669 {
670     PKTHREAD Thread;
671 
672     /* Initialize the kernel for the current CPU */
673     KiInitializeKernel(&KiInitialProcess.Pcb,
674                        (PKTHREAD)KeLoaderBlock->Thread,
675                        (PVOID)(KeLoaderBlock->KernelStack & ~3),
676                        (PKPRCB)__readfsdword(KPCR_PRCB),
677                        KeNumberProcessors - 1,
678                        KeLoaderBlock);
679 
680     /* Set the priority of this thread to 0 */
681     Thread = KeGetCurrentThread();
682     Thread->Priority = 0;
683 
684     /* Force interrupts enabled and lower IRQL back to DISPATCH_LEVEL */
685     _enable();
686     KeLowerIrql(DISPATCH_LEVEL);
687 
688     /* Set the right wait IRQL */
689     Thread->WaitIrql = DISPATCH_LEVEL;
690 
691     /* Jump into the idle loop */
692     KiIdleLoop();
693 }
694 
695 static
696 VOID
697 KiMarkPageAsReadOnly(
698     PVOID Address)
699 {
700     PHARDWARE_PTE PointerPte;
701 
702     /* Make sure the address is page aligned */
703     ASSERT(ALIGN_DOWN_POINTER_BY(Address, PAGE_SIZE) == Address);
704 
705     /* Get the PTE address */
706     PointerPte = ((PHARDWARE_PTE)PTE_BASE) + ((ULONG_PTR)Address / PAGE_SIZE);
707     ASSERT(PointerPte->Valid);
708     ASSERT(PointerPte->Write);
709 
710     /* Set as read-only */
711     PointerPte->Write = 0;
712 
713     /* Flush the TLB entry */
714     __invlpg(Address);
715 }
716 
717 CODE_SEG("INIT")
718 DECLSPEC_NORETURN
719 VOID
720 NTAPI
721 KiSystemStartup(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
722 {
723     ULONG Cpu;
724     PKTHREAD InitialThread;
725     ULONG InitialStack;
726     PKGDTENTRY Gdt;
727     PKIDTENTRY Idt;
728     KIDTENTRY NmiEntry, DoubleFaultEntry;
729     PKTSS Tss;
730     PKIPCR Pcr;
731     KIRQL DummyIrql;
732 
733     /* Boot cycles timestamp */
734     BootCycles = __rdtsc();
735 
736     /* Save the loader block and get the current CPU */
737     KeLoaderBlock = LoaderBlock;
738     Cpu = KeNumberProcessors;
739     if (!Cpu)
740     {
741         /* If this is the boot CPU, set FS and the CPU Number*/
742         Ke386SetFs(KGDT_R0_PCR);
743         __writefsdword(KPCR_PROCESSOR_NUMBER, Cpu);
744 
745         /* Set the initial stack and idle thread as well */
746         LoaderBlock->KernelStack = (ULONG_PTR)P0BootStack;
747         LoaderBlock->Thread = (ULONG_PTR)&KiInitialThread;
748     }
749 
750     /* Save the initial thread and stack */
751     InitialStack = LoaderBlock->KernelStack;
752     InitialThread = (PKTHREAD)LoaderBlock->Thread;
753 
754     /* Clean the APC List Head */
755     InitializeListHead(&InitialThread->ApcState.ApcListHead[KernelMode]);
756 
757     /* Initialize the machine type */
758     KiInitializeMachineType();
759 
760     /* Skip initial setup if this isn't the Boot CPU */
761     if (Cpu) goto AppCpuInit;
762 
763     /* Get GDT, IDT, PCR and TSS pointers */
764     KiGetMachineBootPointers(&Gdt, &Idt, &Pcr, &Tss);
765 
766     /* Setup the TSS descriptors and entries */
767     Ki386InitializeTss(Tss, Idt, Gdt);
768 
769     /* Initialize the PCR */
770     RtlZeroMemory(Pcr, PAGE_SIZE);
771     KiInitializePcr(Cpu,
772                     Pcr,
773                     Idt,
774                     Gdt,
775                     Tss,
776                     InitialThread,
777                     (PVOID)KiDoubleFaultStack);
778 
779     /* Set us as the current process */
780     InitialThread->ApcState.Process = &KiInitialProcess.Pcb;
781 
782     /* Clear DR6/7 to cleanup bootloader debugging */
783     __writefsdword(KPCR_TEB, 0);
784     __writefsdword(KPCR_DR6, 0);
785     __writefsdword(KPCR_DR7, 0);
786 
787     /* Setup the IDT */
788     KeInitExceptions();
789 
790     /* Load Ring 3 selectors for DS/ES */
791     Ke386SetDs(KGDT_R3_DATA | RPL_MASK);
792     Ke386SetEs(KGDT_R3_DATA | RPL_MASK);
793 
794     /* Save NMI and double fault traps */
795     RtlCopyMemory(&NmiEntry, &Idt[2], sizeof(KIDTENTRY));
796     RtlCopyMemory(&DoubleFaultEntry, &Idt[8], sizeof(KIDTENTRY));
797 
798     /* Copy kernel's trap handlers */
799     RtlCopyMemory(Idt,
800                   (PVOID)KiIdtDescriptor.Base,
801                   KiIdtDescriptor.Limit + 1);
802 
803     /* Restore NMI and double fault */
804     RtlCopyMemory(&Idt[2], &NmiEntry, sizeof(KIDTENTRY));
805     RtlCopyMemory(&Idt[8], &DoubleFaultEntry, sizeof(KIDTENTRY));
806 
807 AppCpuInit:
808     /* Loop until we can release the freeze lock */
809     do
810     {
811         /* Loop until execution can continue */
812         while (*(volatile PKSPIN_LOCK*)&KiFreezeExecutionLock == (PVOID)1);
813     } while(InterlockedBitTestAndSet((PLONG)&KiFreezeExecutionLock, 0));
814 
815     /* Setup CPU-related fields */
816     __writefsdword(KPCR_NUMBER, Cpu);
817     __writefsdword(KPCR_SET_MEMBER, 1 << Cpu);
818     __writefsdword(KPCR_SET_MEMBER_COPY, 1 << Cpu);
819     __writefsdword(KPCR_PRCB_SET_MEMBER, 1 << Cpu);
820 
821     KiVerifyCpuFeatures(Pcr->Prcb);
822 
823     /* Initialize the Processor with HAL */
824     HalInitializeProcessor(Cpu, KeLoaderBlock);
825 
826     /* Set active processors */
827     KeActiveProcessors |= __readfsdword(KPCR_SET_MEMBER);
828     KeNumberProcessors++;
829 
830     /* Check if this is the boot CPU */
831     if (!Cpu)
832     {
833         /* Initialize debugging system */
834         KdInitSystem(0, KeLoaderBlock);
835 
836         /* Check for break-in */
837         if (KdPollBreakIn()) DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
838 
839         /* Make the lowest page of the boot and double fault stack read-only */
840         KiMarkPageAsReadOnly(P0BootStackData);
841         KiMarkPageAsReadOnly(KiDoubleFaultStackData);
842     }
843 
844     /* Raise to HIGH_LEVEL */
845     KeRaiseIrql(HIGH_LEVEL, &DummyIrql);
846 
847     /* Switch to new kernel stack and start kernel bootstrapping */
848     KiSwitchToBootStack(InitialStack & ~3);
849 }
850