3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/i386/kernel.c
6 * PURPOSE: Initializes the kernel
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
16 #include <internal/napi.h>
18 /* GLOBALS *******************************************************************/
20 PKPRCB KiProcessorBlock
[MAXIMUM_PROCESSORS
];
22 PKNODE KeNodeBlock
[1];
23 UCHAR KeNumberNodes
= 1;
24 UCHAR KeProcessNodeSeed
;
25 ETHREAD KiInitialThread
;
26 EPROCESS KiInitialProcess
;
28 extern ULONG Ke386GlobalPagesEnabled
;
29 extern PVOID trap_stack
, init_stack
;
31 /* System-defined Spinlocks */
32 KSPIN_LOCK KiDispatcherLock
;
34 KSPIN_LOCK MmSystemSpaceLock
;
35 KSPIN_LOCK CcBcbSpinLock
;
36 KSPIN_LOCK CcMasterSpinLock
;
37 KSPIN_LOCK CcVacbSpinLock
;
38 KSPIN_LOCK CcWorkQueueSpinLock
;
39 KSPIN_LOCK NonPagedPoolLock
;
40 KSPIN_LOCK MmNonPagedPoolLock
;
41 KSPIN_LOCK IopCancelSpinLock
;
42 KSPIN_LOCK IopVpbSpinLock
;
43 KSPIN_LOCK IopDatabaseLock
;
44 KSPIN_LOCK IopCompletionLock
;
45 KSPIN_LOCK NtfsStructLock
;
46 KSPIN_LOCK AfdWorkQueueSpinLock
;
47 KSPIN_LOCK KiTimerTableLock
[16];
48 KSPIN_LOCK KiReverseStallIpiLock
;
50 KSPIN_LOCK KiFreezeExecutionLock
;
51 KSPIN_LOCK Ki486CompatibilityLock
;
53 #if defined (ALLOC_PRAGMA)
54 #pragma alloc_text(INIT, KeInit1)
55 #pragma alloc_text(INIT, KeInit2)
58 /* FUNCTIONS *****************************************************************/
66 /* Initialize Bugcheck Callback data */
67 InitializeListHead(&BugcheckCallbackListHead
);
68 InitializeListHead(&BugcheckReasonCallbackListHead
);
69 KeInitializeSpinLock(&BugCheckCallbackLock
);
71 /* Initialize the Timer Expiration DPC */
72 KeInitializeDpc(&KiExpireTimerDpc
, KiExpireTimers
, NULL
);
73 KeSetTargetProcessorDpc(&KiExpireTimerDpc
, 0);
75 /* Initialize Profiling data */
76 KeInitializeSpinLock(&KiProfileLock
);
77 InitializeListHead(&KiProfileListHead
);
78 InitializeListHead(&KiProfileSourceListHead
);
80 /* Loop the timer table */
81 for (i
= 0; i
< TIMER_TABLE_SIZE
; i
++)
83 /* Initialize the list and entries */
84 InitializeListHead(&KiTimerTableListHead
[i
].Entry
);
85 KiTimerTableListHead
[i
].Time
.HighPart
= 0xFFFFFFFF;
86 KiTimerTableListHead
[i
].Time
.LowPart
= 0;
89 /* Initialize old-style list */
90 InitializeListHead(&KiTimerListHead
);
92 /* Initialize the Swap event and all swap lists */
93 KeInitializeEvent(&KiSwapEvent
, SynchronizationEvent
, FALSE
);
94 InitializeListHead(&KiProcessInSwapListHead
);
95 InitializeListHead(&KiProcessOutSwapListHead
);
96 InitializeListHead(&KiStackInSwapListHead
);
98 /* Initialize the mutex for generic DPC calls */
99 KeInitializeMutex(&KiGenericCallDpcMutex
, 0);
101 /* Initialize the syscall table */
102 KeServiceDescriptorTable
[0].Base
= MainSSDT
;
103 KeServiceDescriptorTable
[0].Count
= NULL
;
104 KeServiceDescriptorTable
[0].Limit
= NUMBER_OF_SYSCALLS
;
105 KeServiceDescriptorTable
[1].Limit
= 0;
106 KeServiceDescriptorTable
[0].Number
= MainSSPT
;
108 /* Copy the the current table into the shadow table for win32k */
109 RtlCopyMemory(KeServiceDescriptorTableShadow
,
110 KeServiceDescriptorTable
,
111 sizeof(KeServiceDescriptorTable
));
116 KiComputeReciprocal(IN LONG Divisor
,
119 LARGE_INTEGER Reciprocal
= {{0}};
120 LONG BitCount
= 0, Remainder
= 1;
122 /* Start by calculating the remainder */
123 while (Reciprocal
.HighPart
>= 0)
125 /* Increase the loop (bit) count */
128 /* Calculate the current fraction */
129 Reciprocal
.HighPart
= (Reciprocal
.HighPart
<< 1) |
130 (Reciprocal
.LowPart
>> 31);
131 Reciprocal
.LowPart
<<= 1;
133 /* Double the remainder and see if we went past the divisor */
135 if (Remainder
>= Divisor
)
137 /* Set the low-bit and calculate the new remainder */
138 Remainder
-= Divisor
;
139 Reciprocal
.LowPart
|= 1;
143 /* Check if we have a remainder */
146 /* Check if the current fraction value is too large */
147 if ((Reciprocal
.LowPart
== 0xFFFFFFFF) &&
148 (Reciprocal
.HighPart
== 0xFFFFFFFF))
150 /* Set the high bit and reduce the bit count */
151 Reciprocal
.LowPart
= 0;
152 Reciprocal
.HighPart
= 0x80000000;
157 /* Check if only the lowest bits got too large */
158 if (Reciprocal
.LowPart
== 0xFFFFFFFF)
160 /* Reset them and increase the high bits instead */
161 Reciprocal
.LowPart
= 0;
162 Reciprocal
.HighPart
++;
166 /* All is well, increase the low bits */
167 Reciprocal
.LowPart
++;
172 /* Now calculate the actual shift and return the reciprocal */
173 *Shift
= (UCHAR
)BitCount
- 64;
179 KiInitSpinLocks(IN PKPRCB Prcb
,
184 /* Initialize Dispatcher Fields */
185 Prcb
->QueueIndex
= 1;
186 Prcb
->ReadySummary
= 0;
187 Prcb
->DeferredReadyListHead
.Next
= NULL
;
188 for (i
= 0; i
< 32; i
++)
190 /* Initialize the ready list */
191 InitializeListHead(&Prcb
->DispatcherReadyListHead
[i
]);
194 /* Initialize DPC Fields */
195 InitializeListHead(&Prcb
->DpcData
[DPC_NORMAL
].DpcListHead
);
196 KeInitializeSpinLock(&Prcb
->DpcData
[DPC_NORMAL
].DpcLock
);
197 Prcb
->DpcData
[DPC_NORMAL
].DpcQueueDepth
= 0;
198 Prcb
->DpcData
[DPC_NORMAL
].DpcCount
= 0;
199 Prcb
->DpcRoutineActive
= FALSE
;
200 Prcb
->MaximumDpcQueueDepth
= KiMaximumDpcQueueDepth
;
201 Prcb
->MinimumDpcRate
= KiMinimumDpcRate
;
202 Prcb
->AdjustDpcThreshold
= KiAdjustDpcThreshold
;
203 KeInitializeDpc(&Prcb
->CallDpc
, NULL
, NULL
);
204 KeSetTargetProcessorDpc(&Prcb
->CallDpc
, Number
);
205 KeSetImportanceDpc(&Prcb
->CallDpc
, HighImportance
);
207 /* Initialize the Wait List Head */
208 InitializeListHead(&Prcb
->WaitListHead
);
210 /* Initialize Queued Spinlocks */
211 Prcb
->LockQueue
[LockQueueDispatcherLock
].Next
= NULL
;
212 Prcb
->LockQueue
[LockQueueDispatcherLock
].Lock
= &KiDispatcherLock
;
213 Prcb
->LockQueue
[LockQueueExpansionLock
].Next
= NULL
;
214 Prcb
->LockQueue
[LockQueueExpansionLock
].Lock
= NULL
;
215 Prcb
->LockQueue
[LockQueuePfnLock
].Next
= NULL
;
216 Prcb
->LockQueue
[LockQueuePfnLock
].Lock
= &MmPfnLock
;
217 Prcb
->LockQueue
[LockQueueSystemSpaceLock
].Next
= NULL
;
218 Prcb
->LockQueue
[LockQueueSystemSpaceLock
].Lock
= &MmSystemSpaceLock
;
219 Prcb
->LockQueue
[LockQueueBcbLock
].Next
= NULL
;
220 Prcb
->LockQueue
[LockQueueBcbLock
].Lock
= &CcBcbSpinLock
;
221 Prcb
->LockQueue
[LockQueueMasterLock
].Next
= NULL
;
222 Prcb
->LockQueue
[LockQueueMasterLock
].Lock
= &CcMasterSpinLock
;
223 Prcb
->LockQueue
[LockQueueVacbLock
].Next
= NULL
;
224 Prcb
->LockQueue
[LockQueueVacbLock
].Lock
= &CcVacbSpinLock
;
225 Prcb
->LockQueue
[LockQueueWorkQueueLock
].Next
= NULL
;
226 Prcb
->LockQueue
[LockQueueWorkQueueLock
].Lock
= &CcWorkQueueSpinLock
;
227 Prcb
->LockQueue
[LockQueueNonPagedPoolLock
].Next
= NULL
;
228 Prcb
->LockQueue
[LockQueueNonPagedPoolLock
].Lock
= &NonPagedPoolLock
;
229 Prcb
->LockQueue
[LockQueueMmNonPagedPoolLock
].Next
= NULL
;
230 Prcb
->LockQueue
[LockQueueMmNonPagedPoolLock
].Lock
= &MmNonPagedPoolLock
;
231 Prcb
->LockQueue
[LockQueueIoCancelLock
].Next
= NULL
;
232 Prcb
->LockQueue
[LockQueueIoCancelLock
].Lock
= &IopCancelSpinLock
;
233 Prcb
->LockQueue
[LockQueueIoVpbLock
].Next
= NULL
;
234 Prcb
->LockQueue
[LockQueueIoVpbLock
].Lock
= &IopVpbSpinLock
;
235 Prcb
->LockQueue
[LockQueueIoDatabaseLock
].Next
= NULL
;
236 Prcb
->LockQueue
[LockQueueIoDatabaseLock
].Lock
= &IopDatabaseLock
;
237 Prcb
->LockQueue
[LockQueueIoCompletionLock
].Next
= NULL
;
238 Prcb
->LockQueue
[LockQueueIoCompletionLock
].Lock
= &IopCompletionLock
;
239 Prcb
->LockQueue
[LockQueueNtfsStructLock
].Next
= NULL
;
240 Prcb
->LockQueue
[LockQueueNtfsStructLock
].Lock
= &NtfsStructLock
;
241 Prcb
->LockQueue
[LockQueueAfdWorkQueueLock
].Next
= NULL
;
242 Prcb
->LockQueue
[LockQueueAfdWorkQueueLock
].Lock
= &AfdWorkQueueSpinLock
;
243 Prcb
->LockQueue
[LockQueueUnusedSpare16
].Next
= NULL
;
244 Prcb
->LockQueue
[LockQueueUnusedSpare16
].Lock
= NULL
;
246 /* Loop timer locks */
247 for (i
= 0; i
< LOCK_QUEUE_TIMER_TABLE_LOCKS
; i
++)
249 /* Initialize the lock and setup the Queued Spinlock */
250 KeInitializeSpinLock(&KiTimerTableLock
[i
]);
251 Prcb
->LockQueue
[i
].Next
= NULL
;
252 Prcb
->LockQueue
[i
].Lock
= &KiTimerTableLock
[i
];
255 /* Check if this is the boot CPU */
258 /* Initialize the lock themselves */
259 KeInitializeSpinLock(&KiDispatcherLock
);
260 KeInitializeSpinLock(&KiReverseStallIpiLock
);
261 KeInitializeSpinLock(&MmPfnLock
);
262 KeInitializeSpinLock(&MmSystemSpaceLock
);
263 KeInitializeSpinLock(&CcBcbSpinLock
);
264 KeInitializeSpinLock(&CcMasterSpinLock
);
265 KeInitializeSpinLock(&CcVacbSpinLock
);
266 KeInitializeSpinLock(&CcWorkQueueSpinLock
);
267 KeInitializeSpinLock(&IopCancelSpinLock
);
268 KeInitializeSpinLock(&IopCompletionLock
);
269 KeInitializeSpinLock(&IopDatabaseLock
);
270 KeInitializeSpinLock(&IopVpbSpinLock
);
271 KeInitializeSpinLock(&NonPagedPoolLock
);
272 KeInitializeSpinLock(&MmNonPagedPoolLock
);
273 KeInitializeSpinLock(&NtfsStructLock
);
274 KeInitializeSpinLock(&AfdWorkQueueSpinLock
);
275 KeInitializeDispatcher(); // ROS OLD DISPATCHER
281 KiInitializePcr(IN ULONG ProcessorNumber
,
286 IN PKTHREAD IdleThread
,
290 Pcr
->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
291 Pcr
->NtTib
.StackBase
= 0;
292 Pcr
->NtTib
.StackLimit
= 0;
295 /* Set the Current Thread */
296 //Pcr->PrcbData.CurrentThread = IdleThread;
298 /* Set pointers to ourselves */
299 Pcr
->Self
= (PKPCR
)Pcr
;
300 Pcr
->Prcb
= &Pcr
->PrcbData
;
302 /* Set the PCR Version */
303 Pcr
->MajorVersion
= PCR_MAJOR_VERSION
;
304 Pcr
->MinorVersion
= PCR_MINOR_VERSION
;
306 /* Set the PCRB Version */
307 Pcr
->PrcbData
.MajorVersion
= 1;
308 Pcr
->PrcbData
.MinorVersion
= 1;
310 /* Set the Build Type */
311 Pcr
->PrcbData
.BuildType
= 0;
313 /* Set the Processor Number and current Processor Mask */
314 Pcr
->PrcbData
.Number
= (UCHAR
)ProcessorNumber
;
315 Pcr
->PrcbData
.SetMember
= 1 << ProcessorNumber
;
317 /* Set the PRCB for this Processor */
318 KiProcessorBlock
[ProcessorNumber
] = Pcr
->Prcb
;
320 /* Start us out at PASSIVE_LEVEL */
321 Pcr
->Irql
= PASSIVE_LEVEL
;
323 /* Set the GDI, IDT, TSS and DPC Stack */
324 Pcr
->GDT
= (PVOID
)Gdt
;
327 Pcr
->PrcbData
.DpcStack
= DpcStack
;
332 KiInitializeKernel(IN PKPROCESS InitProcess
,
333 IN PKTHREAD InitThread
,
337 IN PROS_LOADER_PARAMETER_BLOCK LoaderBlock
)
341 LARGE_INTEGER PageDirectory
;
344 /* Detect and set the CPU Type */
345 KiSetProcessorType();
347 /* Set CR0 features based on detected CPU */
350 /* Check if an FPU is present */
351 NpxPresent
= KiIsNpxPresent();
353 /* Initialize the Power Management Support for this PRCB */
354 PoInitializePrcb(Prcb
);
356 /* Bugcheck if this is a 386 CPU */
357 if (Prcb
->CpuType
== 3) KeBugCheckEx(0x5D, 0x386, 0, 0, 0);
359 /* Get the processor features for the CPU */
360 FeatureBits
= KiGetFeatureBits();
362 /* Save feature bits */
363 Prcb
->FeatureBits
= FeatureBits
;
365 /* Get cache line information for this CPU */
366 KiGetCacheInformation();
368 /* Initialize spinlocks and DPC data */
369 KiInitSpinLocks(Prcb
, Number
);
371 /* Check if this is the Boot CPU */
375 KeNodeBlock
[0] = &KiNode0
;
376 Prcb
->ParentNode
= KeNodeBlock
[0];
377 KeNodeBlock
[0]->ProcessorMask
= Prcb
->SetMember
;
379 /* Set boot-level flags */
380 KeI386NpxPresent
= NpxPresent
;
381 KeI386CpuType
= Prcb
->CpuType
;
382 KeI386CpuStep
= Prcb
->CpuStep
;
383 KeProcessorArchitecture
= 0;
384 KeProcessorLevel
= (USHORT
)Prcb
->CpuType
;
385 if (Prcb
->CpuID
) KeProcessorRevision
= Prcb
->CpuStep
;
386 KeFeatureBits
= FeatureBits
;
387 KeI386FxsrPresent
= (KeFeatureBits
& KF_FXSR
) ? TRUE
: FALSE
;
388 KeI386XMMIPresent
= (KeFeatureBits
& KF_XMMI
) ? TRUE
: FALSE
;
390 /* Set the current MP Master KPRCB to the Boot PRCB */
391 Prcb
->MultiThreadSetMaster
= Prcb
;
393 /* Initialize some spinlocks */
394 KeInitializeSpinLock(&KiFreezeExecutionLock
);
395 KeInitializeSpinLock(&Ki486CompatibilityLock
);
397 /* Initialize portable parts of the OS */
400 /* Initialize the Idle Process and the Process Listhead */
401 InitializeListHead(&KiProcessListHead
);
402 PageDirectory
.QuadPart
= 0;
403 KeInitializeProcess(InitProcess
,
407 InitProcess
->QuantumReset
= MAXCHAR
;
412 DPRINT1("SMP Boot support not yet present\n");
416 /* Setup the Idle Thread */
417 KeInitializeThread(InitProcess
,
426 InitThread
->NextProcessor
= Number
;
427 InitThread
->Priority
= HIGH_PRIORITY
;
428 InitThread
->State
= Running
;
429 InitThread
->Affinity
= 1 << Number
;
430 InitThread
->WaitIrql
= DISPATCH_LEVEL
;
431 InitProcess
->ActiveProcessors
= 1 << Number
;
433 /* Set up the thread-related fields in the PRCB */
434 //Prcb->CurrentThread = InitThread;
435 Prcb
->NextThread
= NULL
;
436 //Prcb->IdleThread = InitThread;
438 /* Initialize the Debugger */
439 KdInitSystem (0, &KeLoaderBlock
);
441 /* Initialize the Kernel Executive */
442 ExpInitializeExecutive();
444 /* Only do this on the boot CPU */
447 /* Calculate the time reciprocal */
448 KiTimeIncrementReciprocal
=
449 KiComputeReciprocal(KeMaximumIncrement
,
450 &KiTimeIncrementShiftCount
);
452 /* Update DPC Values in case they got updated by the executive */
453 Prcb
->MaximumDpcQueueDepth
= KiMaximumDpcQueueDepth
;
454 Prcb
->MinimumDpcRate
= KiMinimumDpcRate
;
455 Prcb
->AdjustDpcThreshold
= KiAdjustDpcThreshold
;
457 /* Allocate the DPC Stack */
458 DpcStack
= MmCreateKernelStack(FALSE
);
459 if (!DpcStack
) KeBugCheckEx(NO_PAGES_AVAILABLE
, 1, 0, 0, 0);
460 Prcb
->DpcStack
= DpcStack
;
462 /* Allocate the IOPM save area. */
463 Ki386IopmSaveArea
= ExAllocatePoolWithTag(PagedPool
,
465 TAG('K', 'e', ' ', ' '));
466 if (!Ki386IopmSaveArea
)
468 /* Bugcheck. We need this for V86/VDM support. */
469 KeBugCheckEx(NO_PAGES_AVAILABLE
, 2, PAGE_SIZE
* 2, 0, 0);
473 /* Free Initial Memory */
478 LARGE_INTEGER Timeout
;
479 Timeout
.QuadPart
= 0x7fffffffffffffffLL
;
480 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
483 /* Bug Check and loop forever if anything failed */
490 KiSystemStartup(IN PROS_LOADER_PARAMETER_BLOCK LoaderBlock
)
492 /* Currently hacked for CPU 0 only */
494 PKIPCR Pcr
= (PKIPCR
)KPCR_BASE
;
497 /* Initialize the PCR */
498 RtlZeroMemory(Pcr
, PAGE_SIZE
);
504 &KiInitialThread
.Tcb
,
508 /* Set us as the current process */
509 KiInitialThread
.Tcb
.ApcState
.Process
= &KiInitialProcess
.Pcb
;
511 /* Clear DR6/7 to cleanup bootloader debugging */
512 Pcr
->PrcbData
.ProcessorState
.SpecialRegisters
.KernelDr6
= 0;
513 Pcr
->PrcbData
.ProcessorState
.SpecialRegisters
.KernelDr7
= 0;
515 /* Setup the boot (Freeldr should've done), double fault and NMI TSS */
516 Ki386InitializeTss();
518 /* Setup CPU-related fields */
520 Pcr
->SetMember
= 1 << Cpu
;
521 Pcr
->SetMemberCopy
= 1 << Cpu
;
522 Prcb
->SetMember
= 1 << Cpu
;
524 /* Initialize the Processor with HAL */
525 HalInitializeProcessor(Cpu
, (PLOADER_PARAMETER_BLOCK
)&KeLoaderBlock
);
527 /* Set active processors */
528 KeActiveProcessors
|= Pcr
->SetMember
;
529 KeNumberProcessors
++;
531 /* Raise to HIGH_LEVEL */
532 KfRaiseIrql(HIGH_LEVEL
);
534 /* Call main kernel intialization */
535 KiInitializeKernel(&KiInitialProcess
.Pcb
,
536 &KiInitialThread
.Tcb
,
550 /* Check if Fxsr was found */
551 if (KeI386FxsrPresent
)
553 /* Enable it. FIXME: Send an IPI */
554 Ke386SetCr4(Ke386GetCr4() | X86_CR4_OSFXSR
);
556 /* Check if XMM was found too */
557 if (KeI386XMMIPresent
)
559 /* Enable it: FIXME: Send an IPI. */
560 Ke386SetCr4(Ke386GetCr4() | X86_CR4_OSXMMEXCPT
);
562 /* FIXME: Implement and enable XMM Page Zeroing for Mm */
566 if (KeFeatureBits
& KF_GLOBAL_PAGE
)
569 /* Enable global pages */
570 Ke386GlobalPagesEnabled
= TRUE
;
571 Ke386SaveFlags(Flags
);
572 Ke386DisableInterrupts();
573 Ke386SetCr4(Ke386GetCr4() | X86_CR4_PGE
);
574 Ke386RestoreFlags(Flags
);
577 if (KeFeatureBits
& KF_FAST_SYSCALL
)
579 extern void KiFastCallEntry(void);
581 /* CS Selector of the target segment. */
582 Ke386Wrmsr(0x174, KGDT_R0_CODE
, 0);
584 Ke386Wrmsr(0x175, 0, 0);
586 Ke386Wrmsr(0x176, (ULONG_PTR
)KiFastCallEntry
, 0);
589 /* Does the CPU Support 'prefetchnta' (SSE) */
590 if(KeFeatureBits
& KF_XMMI
)
592 Protect
= MmGetPageProtect(NULL
, (PVOID
)RtlPrefetchMemoryNonTemporal
);
593 MmSetPageProtect(NULL
, (PVOID
)RtlPrefetchMemoryNonTemporal
, Protect
| PAGE_IS_WRITABLE
);
594 /* Replace the ret by a nop */
595 *(PCHAR
)RtlPrefetchMemoryNonTemporal
= 0x90;
596 MmSetPageProtect(NULL
, (PVOID
)RtlPrefetchMemoryNonTemporal
, Protect
);
599 /* Set IDT to writable */
600 Protect
= MmGetPageProtect(NULL
, (PVOID
)KiIdt
);
601 MmSetPageProtect(NULL
, (PVOID
)KiIdt
, Protect
| PAGE_IS_WRITABLE
);