转载自:https://blog.csdn.net/cosmoslife/article/details/7855425
1.2 内核初始化
内核的初始化主要是内核各个组件的初始化,但由于这些内核组件之间有紧密的耦合关系,所以它们的初始化并不是简单地顺序执行初始化。为了解决在初始化过程中的相互依赖性问题,内核的初始化分两个阶段进行,称为阶段0和阶段1。大多数内核组件的初始化函数相应地带有一个整数参数,以指明一次调用是阶段0 初始化还是阶段1 初始化,而有些组件的初始化函数通过检查一个全局变量InitializationPhase 的值来判断当前处于哪个阶段。
我们首先来看阶段0 初始化。阶段0 初始化的目的是,将阶段1 初始化所要用到的基本数据结构建立起来。在阶段0 初始化过程中,中断被禁止,因此处理器可以顺序地执行自己的初始化逻辑。KiSystemStartup 函数首先初始化处理器的状态,包括调整它的IDT,初始化TSS(Task State Segment),以及构造PCR(Processor Control Region)。然后,调用HalInitializeProcessor 函数,为当前处理器初始化其HAL 中的PCR 和处理器间中断向量;接着调用KiInitializeKernel 函数,执行内核初始化。最后,当前线程蜕变成一个空闲线程。
因此,KiInitializeKernel 函数是实际执行内核初始化的函数,其代码位于base\ntos\ke\i386\kernlini.c 文件中。它的职责是:初始化内核数据结构,初始化空闲线程和进程对象,初始化PCR,然后调用执行体初始化函数ExpInitializeExecutive,最后返回。
VOID
KiInitializeKernel (
IN PKPROCESS Process,
IN PKTHREAD Thread,
IN PVOID IdleStack,
IN PKPRCB Prcb,
IN CCHAR Number,
PLOADER_PARAMETER_BLOCK LoaderBlock
)
{
ULONG DirectoryTableBase[2];
PVOID DpcStack;
KIRQL OldIrql;
PKPCR Pcr;
BOOLEAN NpxFlag;
#if !defined(NT_UP)
BOOLEAN FxsrPresent;
BOOLEAN XMMIPresent;
#endif
ULONG FeatureBits;
#if defined(KE_MULTINODE)
LONG Index;
#endif
KiSetProcessorType();
KiSetCR0Bits();
NpxFlag = KiIsNpxPresent();
Pcr = KeGetPcr();
// 初始化处理器的电源状态
PoInitializePrcb(Prcb);
// 检测不支持的处理器的版本
if (Prcb->CpuType == 3) {
KeBugCheckEx(UNSUPPORTED_PROCESSOR,0x386,0,0,0);
}
// 获取处理器相关特性
FeatureBits = KiGetFeatureBits();
//如果主机处理器支持特性,那么在加载选项中设置开启不可执行保护
SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_OPTIN;
if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=ALWAYSON") != NULL) {
SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSON;
FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;
} else if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=OPTOUT") != NULL) {
SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_OPTOUT;
FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;
} else if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=OPTIN") != NULL) {
FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;
} else if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE=ALWAYSOFF") != NULL) {
SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSOFF;
FeatureBits |= KF_GLOBAL_32BIT_EXECUTE;
} else if (strstr(KeLoaderBlock->LoadOptions, "NOEXECUTE") != NULL) {
FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;
} else if (strstr(KeLoaderBlock->LoadOptions, "EXECUTE") != NULL) {
SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSOFF;
FeatureBits |= KF_GLOBAL_32BIT_EXECUTE;
}
#if defined (_X86PAE_)
if ((FeatureBits & KF_NOEXECUTE) == 0) {
FeatureBits &= ~KF_GLOBAL_32BIT_NOEXECUTE;
}
if ((FeatureBits & KF_GLOBAL_32BIT_NOEXECUTE) != 0) {
KiEnableNXSupport();
}
#endif
Prcb->FeatureBits = FeatureBits;
// 初始化PRCB中的ProcessorControlSpace内容,使得本地内核调试器可以像GDT一样获得数据
KiSaveProcessorControlState(&Prcb->ProcessorState);
// 获得处理器缓存大小信息
KiGetCacheInformation();
// 初始化每个处理器的锁数据
KiInitSpinLocks(Prcb, Number);
// 如果初始的处理器已经被初始化,那么初始化每个系统数据结构
if (Number == 0) {
KeNodeBlock[0] = &KiNode0;
#if defined(KE_MULTINODE)
for (Index = 1; Index < MAXIMUM_CCNUMA_NODES; Index++) {
//
// Set temporary node.
//
KeNodeBlock[Index] = &KiNodeInit[Index];
}
#endif
Prcb->ParentNode = KeNodeBlock[0];
KeNodeBlock[0]->ProcessorMask = Prcb->SetMember;
// Initial setting for global Cpu & Stepping levels
KeI386NpxPresent = NpxFlag;
KeI386CpuType = Prcb->CpuType;
KeI386CpuStep = Prcb->CpuStep;
KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
KeProcessorLevel = (USHORT)Prcb->CpuType;
if (Prcb->CpuID == 0) {
KeProcessorRevision = 0xFF00 |
(((Prcb->CpuStep >> 4) + 0xa0 ) & 0x0F0) |
(Prcb->CpuStep & 0xf);
} else {
KeProcessorRevision = Prcb->CpuStep;
}
KeFeatureBits = FeatureBits;
KeI386FxsrPresent = ((KeFeatureBits & KF_FXSR) ? TRUE:FALSE);
KeI386XMMIPresent = ((KeFeatureBits & KF_XMMI) ? TRUE:FALSE);
// As of Windows XP, cmpxchg8b is a required instruction.
if ((KeFeatureBits & KF_
) == 0) {
ULONG Vendor[3];
RtlCopyMemory(Vendor, Prcb->VendorString, sizeof(Vendor));
KeBugCheckEx(UNSUPPORTED_PROCESSOR,
(1 << 24 )
| (Prcb->CpuType << 16) | Prcb->CpuStep,
Vendor[0],
Vendor[1],
Vendor[2]);
}
// 降低IRQL到APC级别
KeLowerIrql(APC_LEVEL);
// 初始化内核自旋锁
KeInitializeSpinLock(&KiFreezeExecutionLock);
// 初始化兼容锁
KeInitializeSpinLock(&Ki486CompatibilityLock);
…
//此处省略部分无关代码
// Performance architecture independent initialization.
KiInitSystem();
// 初始化空闲线程和进程对象
DirectoryTableBase[0] = 0;
DirectoryTableBase[1] = 0;
InitializeListHead(&KiProcessListHead);
KeInitializeProcess(Process,
(KPRIORITY)0,
(KAFFINITY)(0xffffffff),
&DirectoryTableBase[0],
FALSE);
Process->QuantumReset = MAXCHAR; //此处设置为最大值
#if !defined(NT_UP)
//此处都是设置处理器的相关特性,非重点
} else {
// Adjust global cpu setting to represent lowest of all processors
FxsrPresent = ((FeatureBits & KF_FXSR) ? TRUE:FALSE);
if (FxsrPresent != KeI386FxsrPresent) {
// FXSR support must be available on all processors or on none
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_FXSR, 0, 0, 0);
}
XMMIPresent = ((FeatureBits & KF_XMMI) ? TRUE:FALSE);
if (XMMIPresent != KeI386XMMIPresent) {
// XMMI support must be available on all processors or on none
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_XMMI, 0, 0, 0);
}
if (NpxFlag != KeI386NpxPresent) {
// NPX support must be available on all processors or on none
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, 0x387, 0, 0, 0);
}
if ((ULONG)(Prcb->CpuType) != KeI386CpuType) {
if ((ULONG)(Prcb->CpuType) < KeI386CpuType) {
// What is the lowest CPU type
KeI386CpuType = (ULONG)Prcb->CpuType;
KeProcessorLevel = (USHORT)Prcb->CpuType;
}
}
if ((KiBootFeatureBits & KF_CMPXCHG8B) && !(FeatureBits & KF_CMPXCHG8B)) {
// cmpxchg8b must be available on all processors, if installed at boot
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_CMPXCHG8B, 0, 0, 0);
}
if ((KeFeatureBits & KF_GLOBAL_PAGE) && !(FeatureBits & KF_GLOBAL_PAGE)) {
// Global page support must be available on all processors, if on boot processor
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_GLOBAL_PAGE, 0, 0, 0);
}
if ((KeFeatureBits & KF_PAT) && !(FeatureBits & KF_PAT)) {
// PAT must be available on all processors, if on boot processor
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_PAT, 0, 0, 0);
}
if ((KeFeatureBits & KF_MTRR) && !(FeatureBits & KF_MTRR)) {
// MTRR must be available on all processors, if on boot processor
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_MTRR, 0, 0, 0);
}
if ((KeFeatureBits & KF_NOEXECUTE) && !(FeatureBits & KF_NOEXECUTE)) {
// KF_NOEXECUTE must be available on all processors, if on boot processor.
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED,
KF_NOEXECUTE,
0,
0,
0);
if ((KeFeatureBits & KF_FAST_SYSCALL) != (FeatureBits & KF_FAST_SYSCALL)) {
// If this feature is not available on all processors don't use it at all.
KiFastSystemCallDisable = 1;
}
if ((KeFeatureBits & KF_XMMI64) != (FeatureBits & KF_XMMI64)) {
// If not all processors support Streaming SIMD Extensions 64bit FP don't use it at all.
KeFeatureBits &= ~KF_XMMI64;
}
// Use lowest stepping value
if (Prcb->CpuStep < KeI386CpuStep) {
KeI386CpuStep = Prcb->CpuStep;
if (Prcb->CpuID == 0) {
KeProcessorRevision = 0xFF00 |
((Prcb->CpuStep >> 8) + 'A') |
(Prcb->CpuStep & 0xf);
} else {
KeProcessorRevision = Prcb->CpuStep;
}
}
// Use subset of all NT feature bits available on each processor
KeFeatureBits &= FeatureBits;
// 降低IRQL到DISPATCH级别.
KeLowerIrql(DISPATCH_LEVEL);
#endif
}
//更新处理器特性信息
SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
(KeFeatureBits & KF_MMX) ? TRUE : FALSE;
SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
(KeFeatureBits & KF_CMPXCHG8B) ? TRUE : FALSE;
SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI)) ? TRUE : FALSE;
SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI64)) ? TRUE : FALSE;
SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
(KeFeatureBits & KF_3DNOW) ? TRUE : FALSE;
SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
(KeFeatureBits & KF_RDTSC) ? TRUE : FALSE;
//初始化空闲线程对象
//1.初始化内核堆栈为指定的空闲堆栈
//2.设置下一个处理器的序号
//3.设置线程的优先级为最高
//4.设置线程的状态为活动的
//5.将线程关联到指定的处理器上
//6.设置线程的等待中断级别
//7.在进程活动处理器集中设置指定的处理器成员
KeInitializeThread(Thread, (PVOID)((ULONG)IdleStack),(PKSYSTEM_ROUTINE)NULL, (PKSTART_ROUTINE)NULL,
(PVOID)NULL, (PCONTEXT)NULL, (PVOID)NULL, Process);
Thread->NextProcessor = Number;
Thread->Priority = HIGH_PRIORITY;
Thread->State = Running;
Thread->Affinity = (KAFFINITY)(1<<Number);
Thread->WaitIrql = DISPATCH_LEVEL;
SetMember(Number, Process->ActiveProcessors);
// 初始化进程块信息PCR
Prcb->CurrentThread = Thread;
Prcb->NextThread = (PKTHREAD)NULL;
Prcb->IdleThread = Thread;
// 调用执行体初始化函数例程
try {
ExpInitializeExecutive(Number, LoaderBlock);
} except(KeBugCheckEx(PHASE0_EXCEPTION, (ULONG)GetExceptionCode(), (ULONG_PTR)GetExceptionInformation(),
0,0), EXCEPTION_EXECUTE_HANDLER) {
; // 永远不要运行到这里
}
// If the initial processor is being initialized, then compute the timer table reciprocal value and reset the PRCB values for the
// controllable DPC behavior in order to reflect any registry overrides.
if (Number == 0) {
KiTimeIncrementReciprocal = KeComputeReciprocal((LONG)KeMaximumIncrement,&KiTimeIncrementShiftCount);
Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
Prcb->MinimumDpcRate = KiMinimumDpcRate;
Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
// Processor 0's DPC stack was temporarily allocated on the Double Fault Stack, switch to a proper kernel stack now.
DpcStack = MmCreateKernelStack(FALSE, 0);
if (DpcStack == NULL) {
KeBugCheckEx(NO_PAGES_AVAILABLE, 1, 0, 0, 0);
}
Prcb->DpcStack = DpcStack;
// Allocate 8k IOPM bit map saved area to allow BiosCall swap bit maps.
Ki386IopmSaveArea = ExAllocatePoolWithTag(PagedPool,PAGE_SIZE * 2,' eK');
if (Ki386IopmSaveArea == NULL) {
KeBugCheckEx(NO_PAGES_AVAILABLE, 2, PAGE_SIZE * 2, 0, 0);
}
}
// 先提高中断级别,然后再设置指定的空闲进程的优先级别为0
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
KeSetPriorityThread(Thread, (KPRIORITY)0);
//如果线程并未被选中运行在当前的处理器上,那么检查是否还有就绪的线程,否则就将此处理器设置为IdleSummary
KiAcquirePrcbLock(Prcb);
if (Prcb->NextThread == NULL) {
SetMember(Number, KiIdleSummary);
}
KiReleasePrcbLock(Prcb); //释放PRCB锁
KeRaiseIrql(HIGH_LEVEL, &OldIrql); //提高IRQL
// 到此处为止处理器的初始化完成
LoaderBlock->Prcb = (ULONG)NULL;
return;
}
ExpInitializeExecutive 函数的代码位于base\ntos\init\initos.c 文件中,它调用HalInitSystem以初始化HAL,调用ExInitSystem 以初始化执行体组件的各种数据结构,调用MmInitSystem 以初始化内存管理器和内存池,调用ObInitSystem 以初始化对象管理器,调用SeInitSystem 以初始化安全子系统,调用PsInitSystem 以初始化进程/线程管理器,调用PpInitSystem 以初始化即插即用管理器,调用DbgkInitialize 以初始化调试子系统。通常,这些执行体组件的阶段0 初始化相对简单,以初始化组件的内部状态为主,因而经过阶段0 初始化以后仅可以提供最基本的服务。
VOID
ExpInitializeExecutive(
IN ULONG Number,
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
{
PFN_COUNT PagesToBurn;
PCHAR Options;
PCHAR MemoryOption;
NTSTATUS Status;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
PLIST_ENTRY NextEntry;
ANSI_STRING AnsiString;
STRING NameString;
CHAR Buffer[ 256 ];
BOOLEAN BufferSizeOk;
ULONG ImageCount;
ULONG i;
ULONG_PTR ResourceIdPath[3];
PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
PMESSAGE_RESOURCE_DATA MessageData;
PLIST_ENTRY NextMd;
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
if (!ExpIsLoaderValid(LoaderBlock)) {
KeBugCheckEx(MISMATCHED_HAL,
3,
LoaderBlock->Extension->Size,
LoaderBlock->Extension->MajorVersion,
LoaderBlock->Extension->MinorVersion
);
}
// 初始化处理器控制块池旁视指针
#if !defined(_AMD64_)
ExInitPoolLookasidePointers();
#endif
if (Number == 0) {
extern BOOLEAN ExpInTextModeSetup;
// 判断是否为文本模式的安装进程祸远程的引导客户端
ExpInTextModeSetup = FALSE;
IoRemoteBootClient = FALSE;
if (LoaderBlock->SetupLoaderBlock != NULL) {
if ((LoaderBlock->SetupLoaderBlock->Flags & SETUPBLK_FLAGS_IS_TEXTMODE) != 0) {
ExpInTextModeSetup = TRUE;
}
if ((LoaderBlock->SetupLoaderBlock->Flags & SETUPBLK_FLAGS_IS_REMOTE_BOOT) != 0) {
IoRemoteBootClient = TRUE;
ASSERT( _memicmp( LoaderBlock->ArcBootDeviceName, "net(0)", 6 ) == 0 );
}
}
#if defined(REMOTE_BOOT)
SharedUserData->SystemFlags = 0;
if (IoRemoteBootClient) {
SharedUserData->SystemFlags |= SYSTEM_FLAG_REMOTE_BOOT_CLIENT;
}
#endif // defined(REMOTE_BOOT)
// 暗示该代码处于初始化0阶段中
InitializationPhase = 0L;
Options = LoaderBlock->LoadOptions;
if (Options != NULL) {
// If in BBT mode, remove the requested amount of memory from the loader block and use it for BBT purposes instead.
_strupr(Options);
MemoryOption = strstr(Options, "PERFMEM");
if (MemoryOption != NULL) {
MemoryOption = strstr (MemoryOption,"=");
if (MemoryOption != NULL) {
PagesToBurn = (PFN_COUNT) atol (MemoryOption + 1);
// 将MB转换为页数
PagesToBurn *= ((1024 * 1024) / PAGE_SIZE);
if (PagesToBurn != 0) {
PERFINFO_INIT_TRACEFLAGS(Options, MemoryOption);
BBTPagesToReserve = ExBurnMemory (LoaderBlock,
PagesToBurn,
LoaderBBTMemory,
&BBTMemoryDescriptor);
}
}
}
// Burn memory - consume the amount of memory specified in the OS Load Options.
// This is used for testing reduced memory configurations.
MemoryOption = strstr(Options, "BURNMEMORY");
if (MemoryOption != NULL) {
MemoryOption = strstr(MemoryOption,"=");
if (MemoryOption != NULL ) {
PagesToBurn = (PFN_COUNT) atol (MemoryOption + 1);
PagesToBurn *= ((1024 * 1024) / PAGE_SIZE);
if (PagesToBurn != 0) {
ExBurnMemory (LoaderBlock,
PagesToBurn,
LoaderBad,
NULL);
}
}
}
}
// 使用loader加载过的表初始化翻译表
InitNlsTableBase = LoaderBlock->NlsData->AnsiCodePageData;
InitAnsiCodePageDataOffset = 0;
InitOemCodePageDataOffset = (ULONG)((PUCHAR)LoaderBlock->NlsData->OemCodePageData - (PUCHAR)LoaderBlock->NlsData->AnsiCodePageData);
InitUnicodeCaseTableDataOffset = (ULONG)((PUCHAR)LoaderBlock->NlsData->UnicodeCaseTableData - (PUCHAR)LoaderBlock->NlsData->AnsiCodePageData);
RtlInitNlsTables(
(PVOID)((PUCHAR)InitNlsTableBase+InitAnsiCodePageDataOffset),
(PVOID)((PUCHAR)InitNlsTableBase+InitOemCodePageDataOffset),
(PVOID)((PUCHAR)InitNlsTableBase+InitUnicodeCaseTableDataOffset),
&InitTableInfo
);
RtlResetRtlTranslations(&InitTableInfo);
// 1.初始化硬件抽象层
if (HalInitSystem(InitializationPhase, LoaderBlock) == FALSE) {
KeBugCheck(HAL_INITIALIZATION_FAILED);
}
#if defined(_APIC_TPR_)
HalpIRQLToTPR = LoaderBlock->Extension->HalpIRQLToTPR;
HalpVectorToIRQL = LoaderBlock->Extension->HalpVectorToIRQL;
#endif
// HAL已经初始化成功,所以可以开启中断
#if defined(_X86_)
_enable();
#endif
// Set the interrupt time forward so the Win32 tick count will wrap
// within one hour to make rollover errors show up in fewer than 49.7
// days.
#if DBG
KeAdjustInterruptTime((LONGLONG)(MAXULONG - (60 * 60 * 1000)) * 10 * 1000);
#endif
SharedUserData->CryptoExponent = 0;
#if DBG
NtGlobalFlag |= FLG_ENABLE_CLOSE_EXCEPTIONS |
FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
#endif
Status = RtlFormatBuffer2( Buffer,
sizeof(Buffer),
"C:%s",
LoaderBlock->NtBootPathName,
0
);
if (! NT_SUCCESS(Status)) {
KeBugCheck(SESSION3_INITIALIZATION_FAILED);
}
RtlInitString( &AnsiString, Buffer );
Buffer[ --AnsiString.Length ] = '\0';
NtSystemRoot.Buffer = SharedUserData->NtSystemRoot;
NtSystemRoot.MaximumLength = sizeof( SharedUserData->NtSystemRoot );
NtSystemRoot.Length = 0;
Status = RtlAnsiStringToUnicodeString( &NtSystemRoot,
&AnsiString,
FALSE
);
if (!NT_SUCCESS( Status )) {
KeBugCheck(SESSION3_INITIALIZATION_FAILED);
}
// Find the address of BugCheck message block resource and put it in KiBugCodeMessages.
// WARNING: This code assumes that the KLDR_DATA_TABLE_ENTRY for ntoskrnl.exe is always the first in the loaded module list.
DataTableEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
ResourceIdPath[0] = 11;
ResourceIdPath[1] = 1;
ResourceIdPath[2] = 0;
Status = LdrFindResource_U (DataTableEntry->DllBase,
ResourceIdPath,
3,
(VOID *) &ResourceDataEntry);
if (NT_SUCCESS(Status)) {
Status = LdrAccessResource (DataTableEntry->DllBase,
ResourceDataEntry,
&MessageData,
NULL);
if (NT_SUCCESS(Status)) {
KiBugCodeMessages = MessageData;
}
}
#if !defined(NT_UP)
// Verify that the kernel and HAL images are suitable for MP systems.
// N.B. Loading of kernel and HAL symbols now occurs in kdinit.
ImageCount = 0;
NextEntry = LoaderBlock->LoadOrderListHead.Flink;
while ((NextEntry != &LoaderBlock->LoadOrderListHead) && (ImageCount < 2)) {
DataTableEntry = CONTAINING_RECORD(NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
ImageCount += 1;
if ( !MmVerifyImageIsOkForMpUse(DataTableEntry->DllBase) ) {
KeBugCheckEx(UP_DRIVER_ON_MP_SYSTEM,
(ULONG_PTR)DataTableEntry->DllBase,
0,
0,
0);
}
NextEntry = NextEntry->Flink;
}
#endif // !defined(NT_UP)
CmpInitSystemVersion(0, LoaderBlock);
// 2.初始化执行体组件的各种数据结构
if (!ExInitSystem()) {
KeBugCheck(PHASE0_INITIALIZATION_FAILED);
}
// Get multinode configuration (if any).
KeNumaInitialize();
// 3.初始化内存管理器和内存池
if (MmInitSystem (0, LoaderBlock) == FALSE) {
KeBugCheck(PHASE0_INITIALIZATION_FAILED);
}
// 扫描已经加载的模块列表并加载驱动映像符号
ImageCount = 0;
NextEntry = LoaderBlock->LoadOrderListHead.Flink;
while (NextEntry != &LoaderBlock->LoadOrderListHead) {
BufferSizeOk = TRUE;
if (ImageCount >= 2) {
ULONG Count;
WCHAR *Filename;
ULONG Length;
// 获得下一个组件的数据表入口的地址
DataTableEntry = CONTAINING_RECORD(NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
// 通过下一个组件的内核调试器加载符号
if (DataTableEntry->FullDllName.Buffer[0] == L'\\') {
// 完整的全路径名称已经可用
Filename = DataTableEntry->FullDllName.Buffer;
Length = DataTableEntry->FullDllName.Length / sizeof(WCHAR);
if (sizeof(Buffer) < Length + sizeof(ANSI_NULL)) {
// DllName太长了.
BufferSizeOk = FALSE;
} else {
Count = 0;
do {
Buffer[Count++] = (CHAR)*Filename++;
} while (Count < Length);
Buffer[Count] = 0;
}
} else {
// 假设组件为驱动
if (sizeof(Buffer) < 18 + NtSystemRoot.Length / sizeof(WCHAR) - 2
+ DataTableEntry->BaseDllName.Length / sizeof(WCHAR)
+ sizeof(ANSI_NULL)) {
// ignore the driver entry, it must have been corrupt.
BufferSizeOk = FALSE;
} else {
Status = RtlFormatBuffer2 (Buffer,
sizeof(Buffer),
"%ws\\System32\\Drivers\\%wZ",
&SharedUserData->NtSystemRoot[2],
&DataTableEntry->BaseDllName);
if (! NT_SUCCESS(Status)) {
KeBugCheck(PHASE0_INITIALIZATION_FAILED);
}
}
}
if (BufferSizeOk) {
RtlInitString (&NameString, Buffer );
DbgLoadImageSymbols (&NameString,
DataTableEntry->DllBase,
(ULONG)-1);
#if !defined(NT_UP)
if (!MmVerifyImageIsOkForMpUse(DataTableEntry->DllBase)) {
KeBugCheckEx(UP_DRIVER_ON_MP_SYSTEM,(ULONG_PTR)DataTableEntry->DllBase,0,0,0);
}
#endif // NT_UP
}
}
ImageCount += 1;
NextEntry = NextEntry->Flink;
}
// If break after symbol load is specified, then break into the debugger.
if (KdBreakAfterSymbolLoad != FALSE) {
DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
}
// Turn on the headless terminal now, if we are of a sufficiently new vintage of loader
if (LoaderBlock->Extension->Size >= sizeof (LOADER_PARAMETER_EXTENSION)) {
HeadlessInit(LoaderBlock);
}
// 以上的这些字段只被合法的第三方的32位软件所支持
// New code should call NtQueryInformationSystem() to get them.
#if defined(_WIN64)
SharedUserData->Reserved1 = 0x7ffeffff; // 2gb HighestUserAddress
SharedUserData->Reserved3 = 0x80000000; // 2gb SystemRangeStart
#else
// Set the highest user address and the start of the system range in the shared memory block.
// N.B. This is not a constant value if the target system is an x86 with 3gb of user virtual address space.
SharedUserData->Reserved1 = (ULONG)MM_HIGHEST_USER_ADDRESS;
SharedUserData->Reserved3 = (ULONG)MmSystemRangeStart;
#endif
// 将NLS表映射到分页池并重置翻译表.
// 遍历内存描述符并获取NLS数据大小.
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (MemoryDescriptor->MemoryType == LoaderNlsData) {
InitNlsTableSize += MemoryDescriptor->PageCount*PAGE_SIZE;
}
NextMd = MemoryDescriptor->ListEntry.Flink;
}
InitNlsTableBase = ExAllocatePoolWithTag (NonPagedPool,
InitNlsTableSize,
' slN');
if (InitNlsTableBase == NULL) {
KeBugCheck(PHASE0_INITIALIZATION_FAILED);
}
// Copy the NLS data into the dynamic buffer so that we can
// free the buffers allocated by the loader. The loader guarantees
// contiguous buffers and the base of all the tables is the ANSI
// code page data.
RtlCopyMemory (InitNlsTableBase,
LoaderBlock->NlsData->AnsiCodePageData,
InitNlsTableSize);
RtlInitNlsTables ((PVOID)((PUCHAR)InitNlsTableBase+InitAnsiCodePageDataOffset),
(PVOID)((PUCHAR)InitNlsTableBase+InitOemCodePageDataOffset),
(PVOID)((PUCHAR)InitNlsTableBase+InitUnicodeCaseTableDataOffset),
&InitTableInfo);
RtlResetRtlTranslations (&InitTableInfo);
CmpInitSystemVersion(1, NULL);
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
PVOID StackTraceDataBase;
ULONG StackTraceDataBaseLength;
NTSTATUS Status;
StackTraceDataBaseLength = 512 * 1024;
switch ( MmQuerySystemSize() ) {
case MmMediumSystem :
StackTraceDataBaseLength = 1024 * 1024;
break;
case MmLargeSystem :
StackTraceDataBaseLength = 2048 * 1024;
break;
}
StackTraceDataBase = ExAllocatePoolWithTag( NonPagedPool,
StackTraceDataBaseLength,
'catS');
if (StackTraceDataBase != NULL) {
KdPrint(( "INIT: Kernel mode stack back trace enabled "
"with %u KB buffer.\n", StackTraceDataBaseLength / 1024 ));
Status = RtlInitializeStackTraceDataBase (StackTraceDataBase,
StackTraceDataBaseLength,
StackTraceDataBaseLength);
} else {
Status = STATUS_NO_MEMORY;
}
if (!NT_SUCCESS( Status )) {
KdPrint(( "INIT: Unable to initialize stack trace data base - Status == %lx\n", Status ));
}
}
if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
RtlInitializeExceptionLog(MAX_EXCEPTION_LOG);
}
ExInitializeHandleTablePackage();
#if DBG
// Allocate and zero the system service count table.
//
KeServiceDescriptorTable[0].Count = (PULONG)ExAllocatePoolWithTag(NonPagedPool,KiServiceLimit * sizeof(ULONG),'llac');
KeServiceDescriptorTableShadow[0].Count = KeServiceDescriptorTable[0].Count;
if (KeServiceDescriptorTable[0].Count != NULL ) {
RtlZeroMemory((PVOID)KeServiceDescriptorTable[0].Count,
KiServiceLimit * sizeof(ULONG));
}
#endif
if (!ObInitSystem()) {
KeBugCheck(OBJECT_INITIALIZATION_FAILED); //初始化对象管理器
}
if (!SeInitSystem()) {
KeBugCheck(SECURITY_INITIALIZATION_FAILED);//初始化安全子系统
}
if (PsInitSystem(0, LoaderBlock) == FALSE) {
KeBugCheck(PROCESS_INITIALIZATION_FAILED); //初始化进程/线程管理器
}
if (!PpInitSystem()) {
KeBugCheck(PP0_INITIALIZATION_FAILED); //初始化即插即用管理器
}
DbgkInitialize (); //初始化调试子系统
//这些执行体组件的阶段初始化以初始化组件内部状态为主,仅可以提供最基本的服务
// Compute the tick count multiplier that is used for computing the
// windows millisecond tick count and copy the resultant value to
// the memory that is shared between user and kernel mode.
ExpTickCountMultiplier = ExComputeTickCountMultiplier(KeMaximumIncrement);
SharedUserData->TickCountMultiplier = ExpTickCountMultiplier;
// 在共享内存中设置OS的版本号
SharedUserData->NtMajorVersion = NtMajorVersion;
SharedUserData->NtMinorVersion = NtMinorVersion;
// Set the supported image number range used to determine by the loader if a particular image can be executed on the host system.
// Eventually this will need to be dynamically computed. Also set the architecture specific feature bits.
#if defined(_AMD64_)
SharedUserData->ImageNumberLow = IMAGE_FILE_MACHINE_AMD64;
SharedUserData->ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
#elif defined(_X86_)
SharedUserData->ImageNumberLow = IMAGE_FILE_MACHINE_I386;
SharedUserData->ImageNumberHigh = IMAGE_FILE_MACHINE_I386;
#else
#error "no target architecture"
#endif
}
else {
// 初始化硬件抽象层
if (HalInitSystem(InitializationPhase, LoaderBlock) == FALSE) {
KeBugCheck(HAL_INITIALIZATION_FAILED);
}
}
return;
}
阶段0 初始化完成以后,系统的线程调度器开始工作。特别值得一提的是,进程管理器在阶段0 初始化过程(PspInitPhase0)中,除了初始化其内部的状态变量,它也为初始进程创建一个进程对象,并将其命名为“Idle”。另外,它还创建了“System”进程,以及一个系统线程,此系统线程的开始例程为Phase1Initialization 函数。然而,此线程并不立即被执行,因为在阶段0 初始化过程中中断是禁止的。
BOOLEAN
PspInitPhase0 (
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
{
UNICODE_STRING NameString;
OBJECT_ATTRIBUTES ObjectAttributes;
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
HANDLE ThreadHandle;
PETHREAD Thread;
MM_SYSTEMSIZE SystemSize;
ULONG i;
#if DBG
NTSTATUS Status;
#endif
SystemSize = MmQuerySystemSize ();
PspDefaultPagefileLimit = (ULONG)-1;
#ifdef _WIN64
if (sizeof (TEB) > 8192 || sizeof (PEB) > 4096) {
#else
if (sizeof (TEB) > 4096 || sizeof (PEB) > 4096) {
#endif
KeBugCheckEx (PROCESS_INITIALIZATION_FAILED, 99, sizeof (TEB), sizeof (PEB), 99);
}
switch (SystemSize) {
case MmMediumSystem :
PsMinimumWorkingSet += 10;
PsMaximumWorkingSet += 100;
break;
case MmLargeSystem :
PsMinimumWorkingSet += 30;
PsMaximumWorkingSet += 300;
break;
case MmSmallSystem :
default:
break;
}
// 初始化所有的回调数据结构
for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
ExInitializeCallBack (&PspCreateThreadNotifyRoutine[i]); //线程创建回调函数
}
for (i = 0; i < PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
ExInitializeCallBack (&PspCreateProcessNotifyRoutine[i]); //进程创建回调函数
}
for (i = 0; i < PSP_MAX_LOAD_IMAGE_NOTIFY; i++) {
ExInitializeCallBack (&PspLoadImageNotifyRoutine[i]); //映像加载回调函数
}
PsChangeQuantumTable (FALSE, PsRawPrioritySeparation);
// 某些限额会根据需要自动增加
if (PspDefaultNonPagedLimit == 0 && PspDefaultPagedLimit == 0) {
PspDoingGiveBacks = TRUE;
} else {
PspDoingGiveBacks = FALSE;
}
PspDefaultPagedLimit *= PSP_1MB;
PspDefaultNonPagedLimit *= PSP_1MB;
if (PspDefaultPagefileLimit != -1) {
PspDefaultPagefileLimit *= PSP_1MB;
}
// 初始化活动进程列表和互斥体
InitializeListHead (&PsActiveProcessHead);
PspInitializeProcessListLock();
// 初始化进程安全锁
PsIdleProcess = PsGetCurrentProcess();
PspInitializeProcessLock (PsIdleProcess);
ExInitializeRundownProtection (&PsIdleProcess->RundownProtect);
InitializeListHead (&PsIdleProcess->ThreadListHead);
PsIdleProcess->Pcb.KernelTime = 0;
PsIdleProcess->Pcb.KernelTime = 0;
// 初始化关机线程指针
PspShutdownThread = NULL;
// 初始化对象类型原型纪录的相关字段
RtlZeroMemory (&ObjectTypeInitializer, sizeof (ObjectTypeInitializer));
ObjectTypeInitializer.Length = sizeof (ObjectTypeInitializer);
ObjectTypeInitializer.SecurityRequired = TRUE;
ObjectTypeInitializer.PoolType = NonPagedPool;
ObjectTypeInitializer.InvalidAttributes = OBJ_PERMANENT |
OBJ_EXCLUSIVE |
OBJ_OPENIF;
// 创建线程和进程对象.
RtlInitUnicodeString (&NameString, L"Process");
ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_PROCESS_PAGED_CHARGE;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_PROCESS_NONPAGED_CHARGE;
ObjectTypeInitializer.DeleteProcedure = PspProcessDelete;
ObjectTypeInitializer.ValidAccessMask = PROCESS_ALL_ACCESS;
ObjectTypeInitializer.GenericMapping = PspProcessMapping;
if (!NT_SUCCESS (ObCreateObjectType (&NameString,
&ObjectTypeInitializer,
(PSECURITY_DESCRIPTOR) NULL,
&PsProcessType))) {
return FALSE;
}
RtlInitUnicodeString (&NameString, L"Thread");
ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_THREAD_PAGED_CHARGE;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_THREAD_NONPAGED_CHARGE;
ObjectTypeInitializer.DeleteProcedure = PspThreadDelete;
ObjectTypeInitializer.ValidAccessMask = THREAD_ALL_ACCESS;
ObjectTypeInitializer.GenericMapping = PspThreadMapping;
if (!NT_SUCCESS (ObCreateObjectType (&NameString,
&ObjectTypeInitializer,
(PSECURITY_DESCRIPTOR) NULL,
&PsThreadType))) {
return FALSE;
}
//创建作业对象
RtlInitUnicodeString (&NameString, L"Job");
ObjectTypeInitializer.DefaultPagedPoolCharge = 0;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof (EJOB);
ObjectTypeInitializer.DeleteProcedure = PspJobDelete;
ObjectTypeInitializer.CloseProcedure = PspJobClose;
ObjectTypeInitializer.ValidAccessMask = JOB_OBJECT_ALL_ACCESS;
ObjectTypeInitializer.GenericMapping = PspJobMapping;
ObjectTypeInitializer.InvalidAttributes = 0;
if (!NT_SUCCESS (ObCreateObjectType (&NameString,
&ObjectTypeInitializer,
(PSECURITY_DESCRIPTOR) NULL,
&PsJobType))) {
return FALSE;
}
// 初始化作业列表头和互斥体
PspInitializeJobStructures ();
InitializeListHead (&PspWorkingSetChangeHead.Links);
PspInitializeWorkingSetChangeLock ();
PspCidTable = ExCreateHandleTable (NULL); // 此处创建PspCidTable.
if (PspCidTable == NULL) {
return FALSE;
}
// Set PID and TID reuse to strict FIFO. This isn't absolutely needed but it makes tracking audits easier.
ExSetHandleTableStrictFIFO (PspCidTable);
ExRemoveHandleTable (PspCidTable);
#if defined(i386)
// Ldt Initialization
//
if ( !NT_SUCCESS (PspLdtInitialize ()) ) {
return FALSE;
}
//
// Vdm support Initialization
//
if (!NT_SUCCESS (PspVdmInitialize ())) {
return FALSE;
}
#endif
// Initialize Reaper Data Structures
PsReaperListHead.Next = NULL;
ExInitializeWorkItem (&PsReaperWorkItem, PspReaper, NULL);
// 获取系统访问令牌的指针
// 该令牌被引导进程所使用,因此可以从中获取指针
PspBootAccessToken = ExFastRefGetObject (PsIdleProcess->Token);
InitializeObjectAttributes (&ObjectAttributes,
NULL,
0,
NULL,
NULL);
if (!NT_SUCCESS (PspCreateProcess (&PspInitialSystemProcessHandle,
PROCESS_ALL_ACCESS,
&ObjectAttributes,
NULL,
0,
NULL,
NULL,
NULL,
0))) {
return FALSE;
}
if (!NT_SUCCESS (ObReferenceObjectByHandle (PspInitialSystemProcessHandle,
0L,
PsProcessType,
KernelMode,
&PsInitialSystemProcess,
NULL))) {
return FALSE;
}
strcpy((char *) &PsIdleProcess->ImageFileName[0], "Idle");
strcpy((char *) &PsInitialSystemProcess->ImageFileName[0], "System");
// 将进程对象命名为"Idle"和"System"
// The system process can allocate resources, and its name may be queried by NtQueryInfomationProcess and various audits.
// We must explicitly allocate memory for this field of the System EPROCESS, and initialize it appropriately.
// In this case, appropriate initialization means zeroing the memory.
PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag (PagedPool,
sizeof(OBJECT_NAME_INFORMATION),
'aPeS');
if (PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName != NULL) {
RtlZeroMemory (PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName,
sizeof (OBJECT_NAME_INFORMATION));
} else {
return FALSE;
}
// 此处还创建了一个系统线程,此系统线程的开始例程为Phase1Initialization函数
// 但是因为阶段0初始化过程中中断是被禁止的,所以该线程并不会被立即执行
if (!NT_SUCCESS (PsCreateSystemThread (&ThreadHandle,
THREAD_ALL_ACCESS,
&ObjectAttributes,
0L,
NULL,
Phase1Initialization,
(PVOID)LoaderBlock))) {
return FALSE;
}
if (!NT_SUCCESS (ObReferenceObjectByHandle (ThreadHandle,
0L,
PsThreadType,
KernelMode,
&Thread,
NULL))) {
return FALSE;
}
ZwClose (ThreadHandle);
// On checked systems install an image callout routine
#if DBG
Status = PsSetLoadImageNotifyRoutine (PspImageNotifyTest);
if (!NT_SUCCESS (Status)) {
return FALSE;
}
#endif
return TRUE;
}
KiInitializeKernel 函数返回以后,KiSystemStartup 启动中断,将当前的中断请求级别(IRQL,Interrupt Request Level)降低到DISPATCH_LEVEL,从而允许线程调度器选择新的线程。因此,阶段0 初始化完成以后,阶段1 初始化例程Phase1Initialization 得以运行。注意,如果仔细观察KiSystemStartup 汇编函数的代码,可以发现它在跳转到空闲循环KiIdleLoop 以前,要经过一个屏障KiBarrierWait。此屏障对于系统的第一个处理器并不起作用,而仅对后续的处理器起作用。后面我们在讨论其他处理器的初始化过程时将会看到此屏障的影响。
现在我们知道,阶段1 初始化是在System 进程的一个系统线程中运行的。Phase1Initialization 函数调用Phase1InitializationDiscard 执行阶段1 初始化,然后调用MmZeroPageThread 函数,从而此线程蜕变成内存管理器的零页面线程(内存管理器中负责在后台将空闲页面清零的辅助线程,参见4.5 节关于物理内存管理的描述)。在介绍阶段1 初始化的逻辑以前,我们先来看看在多处理器或多核系统上的初始化过程。如图2.16所示,当第一个处理器(也称为引导处理器,或0 号处理器,或P0)执行到阶段1 初始化时,在Phase1InitializationDiscard 的一个特定点上,它调用KeStartAllProcessors 函数,以启动其他的处理器。这些处理器从KiSystemStartup 函数开始执行。
KeStartAllProcessors 函数的代码位于base\ntos\ke\i386\allproc.c 文件中,它设置好前文提到的位于KiSystemStartup 函数结束处的屏障KiBarrierWait , 然后依次调用KiInitProcessor 函数来启动每个处理器。KiInitProcessor 函数为每个处理器构造一份状态信息(KPROCESSOR_STATE 结构),然后调用HalStartNextProcessor 函数启动该处理器。处理器状态信息是通过调用KiInitProcessorState 函数来构造的,在该函数的代码中,我们可以看到,新处理器的起始指令地址(即KPROCESSOR_STATE 结构的ContextFrame.Eip成员)为KiSystemStartup 例程。KeStartAllProcessors 函数在启动了其他所有处理器以后,设置好每个处理器的控制块(PRCB)中的相关信息,并同步所有处理器的性能计数器,最后打开屏障KiBarrierWait,允许其他处理器进入空闲线程循环。这意味着,此后其他的处理器可以按照线程调度器的选择来运行比空闲线程优先级更高的线程了。所以,屏障KiBarrierWait 可以看成是引导处理器对于非引导处理器的一个约束,在放任它们参与线程调度以前执行必要的初始处理。
VOID
KeStartAllProcessors (
VOID
)
//函数功能:
在初始化1阶段即将开始时候被0阶段所调用,该函数为每一个处理器实现了与hal联系的x86指定代码
//返回值:
所有可用的处理器都被作为参数返回到KiSystemStartup
{
KDESCRIPTOR Descriptor;
ULONG NewProcessorNumber;
SIZE_T ProcessorDataSize;
UCHAR NodeNumber = 0;
ULONG IdtOffset;
ULONG GdtOffset;
#if defined(KE_MULTINODE)
USHORT ProcessorId;
PKNODE Node;
NTSTATUS Status;
#endif
//如果RELOCATEPHYSICAL标志被启用,那么就不要启用额外的处理器
if (KeLoaderBlock->LoadOptions != NULL) {
if (strstr(KeLoaderBlock->LoadOptions, "RELOCATEPHYSICAL") != NULL) {
return;
}
}
// If the boot processor has PII spec A27 errata (also present in
// early Pentium Pro chips), then use only one processor to avoid
// unpredictable eflags corruption.
//
// Note this only affects some (but not all) chips @ 333Mhz and below.
if (!(KeFeatureBits & KF_WORKING_PTE)) {
return;
}
…
// Calculate the size of the per processor data. This includes
// PCR (+PRCB)
// TSS
// Idle Thread Object
// NMI TSS
// Double Fault TSS
// Double Fault Stack
// GDT
// IDT
// If this is a multinode system, the KNODE structure is allocated as well.
// It isn't very big so we waste a few bytes for processors that aren't the first in a node.
// A DPC and Idle stack are also allocated but these are done separately.
ProcessorDataSize = ROUNDUP16(sizeof(KPCR)) +
ROUNDUP16(sizeof(KTSS)) +
ROUNDUP16(sizeof(ETHREAD)) +
ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps)) +
ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps)) +
ROUNDUP16(DOUBLE_FAULT_STACK_SIZE);
#if defined(KE_MULTINODE)
ProcessorDataSize += ROUNDUP16(sizeof(KNODE));
#endif
// 增加GDT的大小
GdtOffset = ProcessorDataSize;
_asm {
sgdt Descriptor.Limit
}
ProcessorDataSize += Descriptor.Limit + 1;
// 增加IDT的大小
IdtOffset = ProcessorDataSize;
_asm {
sidt Descriptor.Limit
}
ProcessorDataSize += Descriptor.Limit + 1;
// 设置障碍值防止其他处理器进入空闲线程循环
KiBarrierWait = 1;
//不停地向HAL请求下一个处理器,直到反馈无任何处理器之后才停止
for (NewProcessorNumber = 1;NewProcessorNumber < MAXIMUM_PROCESSORS;NewProcessorNumber++) {
if (! KiInitProcessor(NewProcessorNumber, &NodeNumber, IdtOffset, GdtOffset, ProcessorDataSize) ) {
break;
}
//依次调用KiInitProcessor函数来启动每个处理器,该函数为每个处理器构造一份状态信息(KPROCESSOR_STATE结构)
KiProcessorStartControl = KcStartContinue;
#if defined(KE_MULTINODE)
Node->ProcessorMask |= 1 << NewProcessorNumber;
#endif
// 等待一个处理器在内核中完成初始化.然后再等待其他的
while (*((volatile ULONG *) &KeLoaderBlock->Prcb) != 0) {
KeYieldProcessor();
}
}
KiAllProcessorsStarted(); // 启动所有处理器
KeAdjustInterruptTime (0); // 同步所有处理器的性能计数器
// 到此处开始允许其他处理器进入空闲线程循环
KiBarrierWait = 0;
//屏障KiBarrierWait可以看成是引导处理器对于非引导处理器的一个约束,在放任它们参与线程调度以前执行必要的初始处理
//此后所有的处理器可以按照线程调度器的选择来运行比空闲线程优先级更高的线程了
}
PKPRCB
KiInitProcessorState(
PKPROCESSOR_STATE pProcessorState,
PVOID PerProcessorAllocation,
ULONG NewProcessorNumber,
UCHAR NodeNumber,
ULONG IdtOffset,
ULONG GdtOffset,
PVOID *ppStack,
PVOID *ppDpcStack
)
/*++
函数功能:
在阶段0初始化时初始化处理器的各种状态信息
返回值:
新的处理器的Prcb
--*/
{
KDESCRIPTOR Descriptor;
KDESCRIPTOR TSSDesc, DFTSSDesc, NMITSSDesc, PCRDesc;
PKGDTENTRY pGDT;
PUCHAR pThreadObject;
PULONG pTopOfStack;
PKTSS pTSS;
PUCHAR Base;
PKPRCB NewPrcb;
ULONG xCr0, xCr3, xEFlags;
Base = (PUCHAR)PerProcessorAllocation;
// 设置处理器的GDT.
_asm {
sgdt Descriptor.Limit
}
KiCloneDescriptor (&Descriptor,
&pProcessorState->SpecialRegisters.Gdtr,
Base + GdtOffset);
pGDT = (PKGDTENTRY) pProcessorState->SpecialRegisters.Gdtr.Base;
// 设置处理器的IDT.
_asm {
sidt Descriptor.Limit
}
KiCloneDescriptor (&Descriptor,
&pProcessorState->SpecialRegisters.Idtr,
Base + IdtOffset);
//设置处理器的TSS和PCR.
KiCloneSelector (KGDT_R0_PCR, pGDT, &PCRDesc, Base);
RtlZeroMemory (Base, ROUNDUP16(sizeof(KPCR)));
Base += ROUNDUP16(sizeof(KPCR));
KiCloneSelector (KGDT_TSS, pGDT, &TSSDesc, Base);
Base += ROUNDUP16(sizeof(KTSS));
//空闲线程对象
pThreadObject = Base;
RtlZeroMemory(Base, sizeof(ETHREAD));
Base += ROUNDUP16(sizeof(ETHREAD));
// 不可屏蔽中断的任务状态段和双重故障任务状态段以及相关的堆栈
KiCloneSelector (KGDT_DF_TSS, pGDT, &DFTSSDesc, Base);
Base += ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps));
KiCloneSelector (KGDT_NMI_TSS, pGDT, &NMITSSDesc, Base);
Base += ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps));
Base += DOUBLE_FAULT_STACK_SIZE;
pTSS = (PKTSS)DFTSSDesc.Base;
pTSS->Esp0 = (ULONG)Base;
pTSS->Esp = (ULONG)Base;
pTSS = (PKTSS)NMITSSDesc.Base;
pTSS->Esp0 = (ULONG)Base;
pTSS->Esp = (ULONG)Base;
// 设置其他特殊的寄存器状态
_asm {
mov eax, cr0
and eax, NOT (CR0_AM or CR0_WP)
mov xCr0, eax
mov eax, cr3
mov xCr3, eax
pushfd
pop eax
mov xEFlags, eax
and xEFlags, NOT EFLAGS_INTERRUPT_MASK
}
pProcessorState->SpecialRegisters.Cr0 = xCr0;
pProcessorState->SpecialRegisters.Cr3 = xCr3;
pProcessorState->ContextFrame.EFlags = xEFlags;
pProcessorState->SpecialRegisters.Tr = KGDT_TSS;
pGDT[KGDT_TSS>>3].HighWord.Bytes.Flags1 = 0x89;
// Encode the processor number into the segment limit of the TEB 6 bits in total. 4 in the high and 2 in the low limit.
pGDT[KGDT_R3_TEB>>3].LimitLow = (USHORT)((NewProcessorNumber&0x3)<<(16-2));
pGDT[KGDT_R3_TEB>>3].HighWord.Bits.LimitHi = (NewProcessorNumber>>2);
#if defined(_X86PAE_)
pProcessorState->SpecialRegisters.Cr4 = CR4_PAE;
#endif
// Allocate a DPC stack, idle thread stack and ThreadObject for the new processor.
*ppStack = MmCreateKernelStack (FALSE, NodeNumber);
*ppDpcStack = MmCreateKernelStack (FALSE, NodeNumber);
// Setup context - push variables onto new stack.
pTopOfStack = (PULONG) *ppStack;
pTopOfStack[-1] = (ULONG) KeLoaderBlock;
pProcessorState->ContextFrame.Esp = (ULONG) (pTopOfStack-2);
pProcessorState->ContextFrame.Eip = (ULONG) KiSystemStartup;
pProcessorState->ContextFrame.SegCs = KGDT_R0_CODE;
pProcessorState->ContextFrame.SegDs = KGDT_R3_DATA;
pProcessorState->ContextFrame.SegEs = KGDT_R3_DATA;
pProcessorState->ContextFrame.SegFs = KGDT_R0_PCR;
pProcessorState->ContextFrame.SegSs = KGDT_R0_DATA;
// 初始化新的处理器PCR和PRCB
KiInitializePcr (
(ULONG) NewProcessorNumber,
(PKPCR) PCRDesc.Base,
(PKIDTENTRY) pProcessorState->SpecialRegisters.Idtr.Base,
(PKGDTENTRY) pProcessorState->SpecialRegisters.Gdtr.Base,
(PKTSS) TSSDesc.Base,
(PKTHREAD) pThreadObject,
(PVOID) *ppDpcStack
);
NewPrcb = ((PKPCR)(PCRDesc.Base))->Prcb;
// Assume new processor will be the first processor in its SMT set.
//(Right choice for non SMT processors, adjusted later if not correct).
NewPrcb->MultiThreadSetMaster = NewPrcb;
#if defined(KE_MULTINODE)
// If this is the first processor on this node, use the
// space allocated for KNODE as the KNODE.
if (KeNodeBlock[NodeNumber] == &KiNodeInit[NodeNumber]) {
Node = (PKNODE)Base;
*Node = KiNodeInit[NodeNumber];
KeNodeBlock[NodeNumber] = Node;
}
Base += ROUNDUP16(sizeof(KNODE));
NewPrcb->ParentNode = Node;
#else
NewPrcb->ParentNode = KeNodeBlock[0];
#endif
ASSERT(((PUCHAR)PerProcessorAllocation + GdtOffset) == Base);
// Adjust LoaderBlock so it has the next processors state
KeLoaderBlock->KernelStack = (ULONG) pTopOfStack;
KeLoaderBlock->Thread = (ULONG) pThreadObject;
KeLoaderBlock->Prcb = (ULONG) NewPrcb;
// Get CPUID(1) info from the starting processor.
KiProcessorStartData[0] = 1;
KiProcessorStartControl = KcStartGetId;
return NewPrcb;
}
非引导处理器的初始化过程虽然也执行KiSystemStartup 函数,但其执行逻辑相对要简单很多。同样地,KiSystemStartup 也调用HalInitializeProcessor 和KiInitializeKernel 函数来执行HAL和内核部分的初始化,并且,KiInitializeKernel函数也调用ExpInitializeExecutive,但是,ExpInitializeExecutive 函数仅仅简单地调用HalInitSystem 而已。在这些函数中,Number 参数代表了当前处理器的序号,如果为0,则表明这是引导处理器,否则为非引导处理器。这样的逻辑不难理解:有些初始化过程,特别是内核执行体组件(比如内存管理器和对象管理器)的初始化例程,它们的处理逻辑针对整个系统,而并非某个处理器,所以只需在引导处理器上运行一次即可;而另外有些初始化过程,则是针对单个处理器的初始化(包括设置处理器的中断描述符表、PRCB 的状态等),因而需要针对每个处理器都执行一次。在阅读这些函数的代码时,我们可以清楚地看到这一基本差别.
前面我们提到了阶段1 初始化是在System 进程的一个系统线程中进行的,它在一个恰当的点上调用KeStartAllProcessors 函数以启动其他的处理器。阶段1 初始化是内核的完全初始化,它占据了系统初始化过程中相当一部分时间。在Windows 2000 系统的引导过程中,可以在屏幕上看到一个进度条,它指示了阶段1 初始化的进度;而在WindowsXP/Server 2003 及以后的Windows 系统中,则是一个闪烁的Windows 标志图案出现在屏幕上,而并非进度条。如前所述,阶段1 初始化的主函数为Phase1InitializationDiscard,其代码位于base\ntos\init\initos.c 文件的933~1 865 行。从该函数的代码可以看出,阶段1的初始化进度仍然有一个百分比估计。该函数按以下步骤完成其主要功能:
(1) 设置全局变量InitializationPhase 为1,标志着当前这次系统引导过程进入内核的阶段1 初始化。
(2) 调用HalInitSystem 函数,执行HAL 的阶段1 初始化。
(3) 初始化图形引导驱动程序,显示Windows 启动屏幕,设置引导进度条范围(0,100)。虽然在Windows Server 2003 中已不再显示进度条,但进度指示逻辑仍然保留。
(4) 调用PoInitSystem,完成电源管理器的阶段0 初始化。
(5) 调用HalQueryRealTimeClock,初始化系统时间,这一步必须在HAL 的阶段1 初始化以后才能进行。再调用KeSetSystemTime 以设置内核的系统时间。
(6) 调用KeStartAllProcessors,启动并初始化其他的处理器。启动了这些辅助处理器以后,重新调用HalQueryRealTimeClock 以初始化系统时间,并调用KeSetSystemTime 设置内核的系统时间。
(7) 接下来,调用ObInitSystem,完成对象管理器的阶段1 初始化;调用ExInitSystem,完成执行体组件的阶段1 初始化;调用KeInitSystem,完成微内核部分的初始化;调用KdInitSystem,完成内核调试器的阶段1 初始化;调用SeInitSystem,完成安全子系统的阶段1 初始化。
(8) 调用InbvUpdateProgressBar 函数,更新进度条至10%。
(9) 创建符号链接“\SystemRoot”。
(10) 调用MmInitSystem,完成内存管理器的阶段1 初始化。
(11) 将国家语言支持(NLS)表映射到系统空间中,然后重置翻译表。
(12) 调用CcInitializeCacheManager,初始化缓存管理器。
(13) 调用CmInitSystem1,初始化配置管理器,建立起基本的注册表对象操作,把引导时获得的数据转变成注册表格式。现在,HKLM\SYSTEM 和HKLM\HARDWARE 已可以使用,这一步必须在I/O 子系统初始化之前完成。
(14) 调用CcPfInitializePrefetcher,初始化内核中的预取器(prefetcher)。
(15) 进度条前进到15%。
(16) 调用FsRtlInitSystem,初始化文件系统支持库(FsRtl)。
(17) 调用KdDebuggerInitialize1,即kdcom.dll 中的调试器初始化。
(18) 调用PpInitSystem,初始化即插即用管理器。
(19) 进度条前进到20%。
(20) 调用LpcInitSystem,初始化LPC 子系统。
(21) 现在系统时间已经正常运行,调用ExInitSystemPhase2,再次初始化执行体组件。
(22) 进度条更新范围设置为25~75%。
(23) 调用IoInitSystem,初始化I/O 系统。这是系统引导过程中较为复杂的一部分,将占据进度条50%的范围。IoInitSystem 函数所做的工作包括:I/O 子系统中的状态变量初始化、驱动程序对象类型和设备对象类型的创建、加载“引导-启动”类型的驱动程序、加载“系统-启动”类型的驱动程序,以及WMI 初始化等。详细的执行过程,请参IoInitSystem 函数的代码,位于base\ntos\io\iomgr\ioinit.c 文件中。
(24) 进度条更新范围恢复到0~100%。
(25) 再次调用MmInitSystem,将当前已加载内核模块中的PAGE 段标记为“可换页”。
(26) 进度条前进到80%。
(27) 调用PoInitSystem,完成电源管理器的阶段1 初始化。
(28) 调用PsInitSystem,完成进程管理器的阶段1 初始化。
(29) 进度条前进到85%。
(30) 调用SeRmInitPhase1,执行安全引用监视器(SRM)的阶段1 初始化,包括建安全引用监视器的命令服务线程。该线程创建一个名为“引用监视器命令端口”的LP端口,以接收来自lsass.exe 进程的命令。参见SeRmInitPhase1 函数的代码。
(31) 进度条前进到90%。
(32) 创建会话管理器子系统进程(smss.exe)。首先准备各种参数信息(RTL_USERPROCESS_PARAMETERS 结构),包括环境参数字符串,然后调用RtlCreateUserProces函数以创建smss 进程。
(33) 进度条前进到100%。
(34) 最后,调用ZwWaitForSingleObject 函数,在smss 进程的句柄上等待,超时值设置为5 s 。如果等待成功, 则意味着在5 s 内smss 进程退出了, 于是调用“KeBugCheck(SESSION5_INITIALIZATION_FAILED)”,系统崩溃;若等待超时,则认为会话管理器已经正常运行,于是阶段1 初始化完成,当前线程蜕变成零页面线程。
VOID
Phase1Initialization (
IN PVOID Context
)
{
Phase1InitializationDiscard (Context);
MmZeroPageThread(); //内存管理器在后台将空闲页面清零的辅助线程
return;
}
VOID
Phase1InitializationDiscard (
IN PVOID Context
)
{
PLOADER_PARAMETER_BLOCK LoaderBlock;
PETHREAD Thread;
PKPRCB Prcb;
KPRIORITY Priority;
NTSTATUS Status;
UNICODE_STRING SessionManager;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Address;
SIZE_T Size;
LARGE_INTEGER UniversalTime;
LARGE_INTEGER CmosTime;
LARGE_INTEGER OldTime;
TIME_FIELDS TimeFields;
UNICODE_STRING EnvString, NullString, UnicodeSystemDriveString;
PWSTR Src, Dst;
BOOLEAN ResetActiveTimeBias;
HANDLE NlsSection;
LARGE_INTEGER SectionSize;
LARGE_INTEGER SectionOffset;
PVOID SectionBase;
PVOID ViewBase;
ULONG CacheViewSize;
SIZE_T CapturedViewSize;
ULONG SavedViewSize;
LONG BootTimeZoneBias;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
#ifndef NT_UP
PMESSAGE_RESOURCE_ENTRY MessageEntry1;
#endif
PCHAR MPKernelString;
PCHAR Options;
PCHAR YearOverrideOption;
LONG CurrentYear = 0;
BOOLEAN NOGUIBOOT;
BOOLEAN SOS;
PVOID Environment;
PRTL_USER_PROCESS_INFORMATION ProcessInformation;
ProcessInformation = ExAllocatePoolWithTag(NonPagedPool,
sizeof(*ProcessInformation),
'tinI'); //分配内存给进程信息
if (ProcessInformation == NULL) {
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,
STATUS_NO_MEMORY,
8,
0,
0); //如果进程信息初始化失败,就抛出内存异常,系统BSOD
}
// The following is a dummy reference section to inline functions that need to have a reference forced.
// This code is never executed, but the compiler can never assume it isn't.
// N.B. The control variable is always false.
if (InitForceInline == TRUE) {
KGUARDED_MUTEX Mutex;
extern ULONG volatile *VpPoolHitTag;
KeTryToAcquireGuardedMutex(&Mutex);
KeEnterGuardedRegion();
KeLeaveGuardedRegion();
KeAreApcsDisabled();
KeRaiseIrqlToDpcLevel();
VpPoolHitTag = &PoolHitTag;
} //此处的代码永远不会被执行
ResetActiveTimeBias = FALSE;
InitializationPhase = 1; //设置该全局变量为,标志着当前系统引导过程进入内核的阶段初始化
Thread = PsGetCurrentThread();
Priority = KeSetPriorityThread( &Thread->Tcb,MAXIMUM_PRIORITY - 1 ); //此处提高当前线程到最高优先级,避免在初始化过程中被预先清空
LoaderBlock = (PLOADER_PARAMETER_BLOCK)Context;
if (HalInitSystem(InitializationPhase, LoaderBlock) == FALSE) { //执行HAL的阶段初始化
KeBugCheck(HAL1_INITIALIZATION_FAILED); //失败即BSOD
}
// 允许图形引导驱动根据OsLoadOptions的值给出不同的显示
Options = LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL;
if (Options) {
NOGUIBOOT = (BOOLEAN)(strstr(Options, "NOGUIBOOT") != NULL);
} else {
NOGUIBOOT = FALSE;
}
InbvEnableBootDriver((BOOLEAN)!NOGUIBOOT);
// 执行到此处时候已经有足够的功能启动系统的图形引导驱动程序
InbvDriverInitialize(LoaderBlock, 18); //初始化图形引导驱动程序
if (NOGUIBOOT) {
InbvNotifyDisplayOwnershipLost(NULL); //如果用户指定了进行的是无界面的引导那么无需图形引导驱动,直接释放显示控制权
}
if (Options) {
SOS = (BOOLEAN)(strstr(Options, "SOS") != NULL);
} else {
SOS = FALSE;
}
if (NOGUIBOOT) {
InbvEnableDisplayString(FALSE);
} else {
InbvEnableDisplayString(SOS);
DisplayBootBitmap(SOS); //显示字符串和位图"SOS"
}
// 检测是否启动进入WinPE
if (Options) {
if (strstr(Options, "MININT") != NULL) {
InitIsWinPEMode = TRUE;
if (strstr(Options, "INRAM") != NULL) {
InitWinPEModeType |= INIT_WINPEMODE_INRAM;
} else {
InitWinPEModeType |= INIT_WINPEMODE_REGULAR;
}
}
}
CmpInitSystemVersion(2, NULL);
// 初始化电源子系统
if (!PoInitSystem(0)) {
KeBugCheck(INTERNAL_POWER_ERROR); //完成电源管理器的阶段初始化
}
// OSLOADOPTIONS可以设置/YEAR=2000.说明这个选项允许用户在拥有中断时钟的硬件层设置一个特定的年份
if (Options) {
YearOverrideOption = strstr(Options, "YEAR");
if (YearOverrideOption != NULL) {
YearOverrideOption = strstr(YearOverrideOption,"=");
}
if (YearOverrideOption != NULL) {
CurrentYear = atol(YearOverrideOption + 1);
}
}
if (ExCmosClockIsSane
&& HalQueryRealTimeClock(&TimeFields)) { //初始化系统时间,必须在HAL的阶段初始化以后才能进行
if (YearOverrideOption) {
TimeFields.Year = (SHORT)CurrentYear; //如果函数初始化成功的话,就赋值当前年份
}
RtlTimeFieldsToTime(&TimeFields, &CmosTime);
UniversalTime = CmosTime;
if ( !ExpRealTimeIsUniversal ) {
// 此处会将设置的本地时间自动转换成通用时间,如果已经设置过时间那么ExpLastTimeZoneBias会包含一个有效的时区数值
if ( ExpLastTimeZoneBias == -1 ) {
ResetActiveTimeBias = TRUE;
ExpLastTimeZoneBias = ExpAltTimeZoneBias;
}
ExpTimeZoneBias.QuadPart = Int32x32To64(
ExpLastTimeZoneBias*60, //秒数的偏差
10000000
);
SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.HighPart;
SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.LowPart;
SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.HighPart;
UniversalTime.QuadPart = CmosTime.QuadPart + ExpTimeZoneBias.QuadPart;
}
KeSetSystemTime(&UniversalTime, &OldTime, FALSE, NULL); //设置内核的系统时间
PoNotifySystemTimeSet(); //通知其他组件系统时间已经被设置
KeBootTime = UniversalTime;
KeBootTimeBias = 0;
}
MPKernelString = "";
CmpInitSystemVersion(8, Options);
#ifndef NT_UP
CmpInitSystemVersion(3, NULL);
KeStartAllProcessors(); //启动并初始化其他处理器
// 在其他辅助处理器之后,重新调用HalQueryRealTimeClock函数初始化系统时间
if (ExCmosClockIsSane
&& HalQueryRealTimeClock(&TimeFields)) {
if (YearOverrideOption) {
TimeFields.Year = (SHORT)CurrentYear;
}
RtlTimeFieldsToTime(&TimeFields, &CmosTime);
if ( !ExpRealTimeIsUniversal ) {
UniversalTime.QuadPart = CmosTime.QuadPart + ExpTimeZoneBias.QuadPart;
}
KeSetSystemTime(&UniversalTime, &OldTime, TRUE, NULL); //再次设置内核的系统时间
}
// 设置系统进程的优先级以及所有的线程
KeSetAffinityProcess(KeGetCurrentThread()->ApcState.Process,
KeActiveProcessors);
DataTableEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
Status = RtlFindMessage (DataTableEntry->DllBase, 11, 0,
WINDOWS_NT_MP_STRING, &MessageEntry1);
if (NT_SUCCESS( Status )) {
MPKernelString = MessageEntry1->Text;
}
else {
MPKernelString = "MultiProcessor Kernel\r\n"; //判断为多处理器内核
}
CmpInitSystemVersion(9, MPKernelString);
#endif
if (!HalAllProcessorsStarted()) {
KeBugCheck(HAL1_INITIALIZATION_FAILED); //此处代码意味着所有的处理器都已经开始运行,而且所有之前的初始化工作都已准备就绪
}
CmpInitSystemVersion(4, DataTableEntry);
if (!ObInitSystem()) {
KeBugCheck(OBJECT1_INITIALIZATION_FAILED); //完成对象管理器的阶段初始化
}
if (!ExInitSystem()) {
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,STATUS_UNSUCCESSFUL,0,1,0); //完成执行体组件的阶段初始化
}
if (!KeInitSystem()) {
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,STATUS_UNSUCCESSFUL,0,2,0); //完成微内核部分的初始化
}
if (!KdInitSystem(InitializationPhase, NULL)) {
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,STATUS_UNSUCCESSFUL,0,3,0); //完成内核调试器的阶段的初始化
}
if (!SeInitSystem()) {
KeBugCheck(SECURITY1_INITIALIZATION_FAILED); //完成安全子系统的阶段的初始化,目录和执行体对象已经可用
// 这个过程必须在设备驱动初始化之前完成
}
InbvUpdateProgressBar(10); //更新进度条到%
// 创建符号链接\SystemRoot
Status = CreateSystemRootLink(LoaderBlock);
if ( !NT_SUCCESS(Status) ) {
KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED,Status,0,0,0);
}
if (MmInitSystem(1, LoaderBlock) == FALSE) {
KeBugCheck(MEMORY1_INITIALIZATION_FAILED); //完成内存管理器的阶段的初始化
}
// 将国家语言支持表,即NLS表映射到系统空间中,然后重置翻译表
SectionSize.HighPart = 0;
SectionSize.LowPart = InitNlsTableSize;
Status = ZwCreateSection(
&NlsSection,
SECTION_ALL_ACCESS,
NULL,
&SectionSize,
PAGE_READWRITE,
SEC_COMMIT,
NULL
);
if (!NT_SUCCESS(Status)) {
KdPrint(("INIT: Nls Section Creation Failed %x\n",Status));
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,1,0,0);
}
Status = ObReferenceObjectByHandle(
NlsSection,
SECTION_ALL_ACCESS,
MmSectionObjectType,
KernelMode,
&InitNlsSectionPointer,
NULL
);
ZwClose(NlsSection);
if ( !NT_SUCCESS(Status) ) {
KdPrint(("INIT: Nls Section Reference Failed %x\n",Status));
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,2,0,0); //判断对Nls段的引用是否正常
}
SectionBase = NULL;
CacheViewSize = SectionSize.LowPart;
SavedViewSize = CacheViewSize;
SectionSize.LowPart = 0;
Status = MmMapViewInSystemCache (InitNlsSectionPointer,
&SectionBase,
&SectionSize,
&CacheViewSize); //映射得到的Nls指针到系统缓存
if (!NT_SUCCESS(Status)) {
KdPrint(("INIT: Map In System Cache Failed %x\n",Status));
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,3,0,0);
}
// 将NLS的数据复制到动态缓冲区,这样可以释放通过loader分配的缓冲区
RtlCopyMemory (SectionBase, InitNlsTableBase, InitNlsTableSize);
//解除视图的映射以移除内存中的所有页面
MmUnmapViewInSystemCache (SectionBase, InitNlsSectionPointer, FALSE);
SectionBase = NULL;
//重新映射如系统缓存,但是需要注意的是此时页面不在有效
Status = MmMapViewInSystemCache(
InitNlsSectionPointer,
&SectionBase,
&SectionSize,
&SavedViewSize
);
if ( !NT_SUCCESS(Status) ) {
KdPrint(("INIT: Map In System Cache Failed %x\n",Status));
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,4,0,0);
}
ExFreePool(InitNlsTableBase); //分配完要释放空间
InitNlsTableBase = SectionBase;
RtlInitNlsTables(
(PVOID)((PUCHAR)InitNlsTableBase+InitAnsiCodePageDataOffset),
(PVOID)((PUCHAR)InitNlsTableBase+InitOemCodePageDataOffset),
(PVOID)((PUCHAR)InitNlsTableBase+InitUnicodeCaseTableDataOffset),
&InitTableInfo
);
RtlResetRtlTranslations(&InitTableInfo);
ViewBase = NULL;
SectionOffset.LowPart = 0;
SectionOffset.HighPart = 0;
CapturedViewSize = 0;
//映射系统dll到用户地址空间
Status = MmMapViewOfSection (InitNlsSectionPointer,
PsGetCurrentProcess(),
&ViewBase,
0L,
0L,
&SectionOffset,
&CapturedViewSize,
ViewShare,
0L,
PAGE_READWRITE);
if (!NT_SUCCESS(Status)) {
KdPrint(("INIT: Map In User Portion Failed %x\n",Status));
KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,5,0,0);
}
RtlCopyMemory (ViewBase, InitNlsTableBase, InitNlsTableSize);
InitNlsTableBase = ViewBase;
if (!CcInitializeCacheManager()) {
KeBugCheck(CACHE_INITIALIZATION_FAILED); //初始化缓存管理器
}
if (!CmInitSystem1(LoaderBlock)) {
KeBugCheck(CONFIG_INITIALIZATION_FAILED);
// 初始化配置管理器,建立基本的注册表对象操作,把引导时获得的数据转换成注册表格式
// 到此为止,HKLM\SYSTEM和HKLM\HARDWARE已经可以使用,这一个步骤必须在I/O子系统初始化之前完成
}
CcPfInitializePrefetcher(); //初始化内核中的预取器(prefetcher)
InbvUpdateProgressBar(15); //进度条前进到%
// 计算时区偏差以及下一个转换日期
BootTimeZoneBias = ExpLastTimeZoneBias;
ExpRefreshTimeZoneInformation(&CmosTime);
if (ResetActiveTimeBias) {
ExLocalTimeToSystemTime(&CmosTime,&UniversalTime);
KeBootTime = UniversalTime;
KeBootTimeBias = 0;
KeSetSystemTime(&UniversalTime, &OldTime, FALSE, NULL);
}
else {
// 检查时区转换是否在启动之前执行
if (BootTimeZoneBias != ExpLastTimeZoneBias) {
ZwSetSystemTime(NULL,NULL);
}
}
if (!FsRtlInitSystem()) {
KeBugCheck(FILE_INITIALIZATION_FAILED); //初始化文件系统支持库(FsRtl)
}
//range list跟PNP管理器有关主要是用于识别、分配硬件资源
//PNP管理器在初始化之前必须初始化range list package
RtlInitializeRangeListPackage();
HalReportResourceUsage();
KdDebuggerInitialize1(LoaderBlock); //kdcom.dll中的调试器初始化
if (!PpInitSystem()) {
KeBugCheck(PP1_INITIALIZATION_FAILED); //初始化即插即用管理器,必须在I/O系统初始化之前完成
}
InbvUpdateProgressBar(20); //进度条更新到%
if (!LpcInitSystem()) {
KeBugCheck(LPC_INITIALIZATION_FAILED);
//初始化LPC子系统,也必须在I/O初始化之前完成
//因为某些驱动会创建系统线程,而系统线程可能会终止从而引发LPC被调用
}
ExInitSystemPhase2();//现在系统时间已经正常运行,再次初始化执行体组件
// Allow time slip notification changes.
KdpTimeSlipPending = 0;
InbvSetProgressBarSubset(25, 75); //进度条更新范围设置为-75%
if (!IoInitSystem(LoaderBlock)) {
KeBugCheck(IO1_INITIALIZATION_FAILED); //初始化I/O系统,这部分非常复杂,将占据进度条%的范围
}
InbvSetProgressBarSubset(0, 100); //进度条更新范围恢复到-100%
CmpInitSystemVersion(6, NULL);
MmInitSystem(2, LoaderBlock); //将当前已加载内核模块中的PAGE段标记为可换页
InbvUpdateProgressBar(80); //进度条前进到%
…
if (!PoInitSystem(1)) {
KeBugCheck(INTERNAL_POWER_ERROR); //完成电源管理器的阶段的初始化
}
if (PsInitSystem(1, LoaderBlock) == FALSE) {
KeBugCheck(PROCESS1_INITIALIZATION_FAILED); //完成进程管理器的阶段的初始化
}
//运行到此处,\SystemRoot已经被初始化定义,可以定位ntdll.dll和smss.exe了
InbvUpdateProgressBar(85); //进度条前进到%
// Force KeBugCheck to look at PsLoadedModuleList now that it is setup.
if (LoaderBlock == KeLoaderBlock) {
KeLoaderBlock = NULL;
}
// 初始化loaderblock
MmFreeLoaderBlock (LoaderBlock);
LoaderBlock = NULL;
Context = NULL;
if (!SeRmInitPhase1()) {
KeBugCheck(REFMON_INITIALIZATION_FAILED);
//执行安全引用监视器(SRM)的阶段的初始化,包括创建安全引用监视器的命令服务线程
//该线程创建一个名为引用监视器命令端口的LPC端口,以接收来自lsass.exe进程的命令
}
InbvUpdateProgressBar(90); //进度条前进到%
// 创建会话管理器子系统进程smss.exe
Size = sizeof( *ProcessParameters ) +
((DOS_MAX_PATH_LENGTH * 6) * sizeof( WCHAR )); //多分配一些命令行参数的空间
ProcessParameters = NULL;
Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
(PVOID *)&ProcessParameters,
0,
&Size,
MEM_COMMIT,
PAGE_READWRITE
);
if (!NT_SUCCESS( Status )) {
KeBugCheckEx(SESSION1_INITIALIZATION_FAILED,Status,0,0,0);
}
//初始化PRTL_USER_PROCESS_PARAMETERS结构中的各种参数信息,包括环境参数字符串
ProcessParameters->Length = (ULONG)Size;
ProcessParameters->MaximumLength = (ULONG)Size;
// Reserve the low 1 MB of address space in the session manager.
// Setup gets started using a replacement for the session manager and that process needs to be able to use the vga driver on x86,
// which uses int10 and thus requires the low 1 meg to be reserved in the process.
// The cost is so low that we just do this all the time, even when setup isn't running.
ProcessParameters->Flags = RTL_USER_PROC_PARAMS_NORMALIZED | RTL_USER_PROC_RESERVE_1MB;
Size = PAGE_SIZE;
Environment = NULL;
Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
&Environment,
0,
&Size,
MEM_COMMIT,
PAGE_READWRITE
); //分配虚拟内存
if (!NT_SUCCESS( Status )) {
KeBugCheckEx(SESSION2_INITIALIZATION_FAILED,Status,0,0,0);
}
ProcessParameters->Environment = Environment;
Dst = (PWSTR)(ProcessParameters + 1);
ProcessParameters->CurrentDirectory.DosPath.Buffer = Dst;
ProcessParameters->CurrentDirectory.DosPath.MaximumLength = DOS_MAX_PATH_LENGTH * sizeof( WCHAR );
//DOS_MAX_PATH_LENGTH = 255+5= 260
RtlCopyUnicodeString( &ProcessParameters->CurrentDirectory.DosPath,
&NtSystemRoot
);
Dst = (PWSTR)((PCHAR)ProcessParameters->CurrentDirectory.DosPath.Buffer +
ProcessParameters->CurrentDirectory.DosPath.MaximumLength
);
ProcessParameters->DllPath.Buffer = Dst;
ProcessParameters->DllPath.MaximumLength = DOS_MAX_PATH_LENGTH * sizeof( WCHAR );
RtlCopyUnicodeString( &ProcessParameters->DllPath,
&ProcessParameters->CurrentDirectory.DosPath
);
RtlAppendUnicodeToString( &ProcessParameters->DllPath, L"\\System32" );
Dst = (PWSTR)((PCHAR)ProcessParameters->DllPath.Buffer +
ProcessParameters->DllPath.MaximumLength
);
ProcessParameters->ImagePathName.Buffer = Dst;
ProcessParameters->ImagePathName.MaximumLength = DOS_MAX_PATH_LENGTH * sizeof( WCHAR );
if (NtInitialUserProcessBufferType != REG_SZ ||
(NtInitialUserProcessBufferLength != (ULONG)-1 &&
(NtInitialUserProcessBufferLength < sizeof(WCHAR) ||
NtInitialUserProcessBufferLength >
sizeof(NtInitialUserProcessBuffer) - sizeof(WCHAR)))) {
KeBugCheckEx(SESSION2_INITIALIZATION_FAILED,
STATUS_INVALID_PARAMETER,
NtInitialUserProcessBufferType,
NtInitialUserProcessBufferLength,
sizeof(NtInitialUserProcessBuffer));
}
// Executable names with spaces don't need to be supported so just find the first space and assume it terminates the process image name.
Src = NtInitialUserProcessBuffer;
while (*Src && *Src != L' ') {
Src++;
}
ProcessParameters->ImagePathName.Length =
(USHORT)((PUCHAR)Src - (PUCHAR)NtInitialUserProcessBuffer);
RtlCopyMemory(ProcessParameters->ImagePathName.Buffer,
NtInitialUserProcessBuffer,
ProcessParameters->ImagePathName.Length);
ProcessParameters->ImagePathName.Buffer[ProcessParameters->ImagePathName.Length / sizeof(WCHAR)] = UNICODE_NULL;
Dst = (PWSTR)((PCHAR)ProcessParameters->ImagePathName.Buffer +
ProcessParameters->ImagePathName.MaximumLength
);
ProcessParameters->CommandLine.Buffer = Dst;
ProcessParameters->CommandLine.MaximumLength = DOS_MAX_PATH_LENGTH * sizeof( WCHAR );
RtlAppendUnicodeToString(&ProcessParameters->CommandLine,
NtInitialUserProcessBuffer);
CmpInitSystemVersion(7, NULL);
NullString.Buffer = L"";
NullString.Length = sizeof(WCHAR);
NullString.MaximumLength = sizeof(WCHAR);
EnvString.Buffer = ProcessParameters->Environment;
EnvString.Length = 0;
EnvString.MaximumLength = (USHORT)Size;
RtlAppendUnicodeToString( &EnvString, L"Path=" );
RtlAppendUnicodeStringToString( &EnvString, &ProcessParameters->DllPath );
RtlAppendUnicodeStringToString( &EnvString, &NullString );
UnicodeSystemDriveString = NtSystemRoot;
UnicodeSystemDriveString.Length = 2 * sizeof( WCHAR );
RtlAppendUnicodeToString( &EnvString, L"SystemDrive=" );
RtlAppendUnicodeStringToString( &EnvString, &UnicodeSystemDriveString );
RtlAppendUnicodeStringToString( &EnvString, &NullString );
RtlAppendUnicodeToString( &EnvString, L"SystemRoot=" );
RtlAppendUnicodeStringToString( &EnvString, &NtSystemRoot );
RtlAppendUnicodeStringToString( &EnvString, &NullString );
SessionManager = ProcessParameters->ImagePathName;
Status = RtlCreateUserProcess(
&SessionManager,
OBJ_CASE_INSENSITIVE,
RtlDeNormalizeProcessParams( ProcessParameters ),
NULL,
NULL,
NULL,
FALSE,
NULL,
NULL,
ProcessInformation); //此处完成smss进程的创建
if (InbvBootDriverInstalled)
{
FinalizeBootLogo(); //一切就绪后初始化启动logo
}
if (!NT_SUCCESS(Status)) {
KeBugCheckEx(SESSION3_INITIALIZATION_FAILED,Status,0,0,0);
}
Status = ZwResumeThread(ProcessInformation->Thread, NULL);
if ( !NT_SUCCESS(Status) ) {
KeBugCheckEx(SESSION4_INITIALIZATION_FAILED,Status,0,0,0);
}
InbvUpdateProgressBar(100); //进度条前进到100%
// 打开调试输出
InbvEnableDisplayString(TRUE);
OldTime.QuadPart = Int32x32To64(5, -(10 * 1000 * 1000));
Status = ZwWaitForSingleObject(ProcessInformation->Process,FALSE,&OldTime); //在smss进程句柄上执行等待指令,超时值设置为s
if (Status == STATUS_SUCCESS) {
KeBugCheck(SESSION5_INITIALIZATION_FAILED);
}
//若等待成功,则意味着s内smss进程退出了,于是系统BSOD;若超时,说明会话管理器已经正常运行,阶段初始化完成
ZwClose( ProcessInformation->Thread );
ZwClose( ProcessInformation->Process ); //关闭线程和进程句柄
//释放传递给会话管理器参数的那部分内存
Size = 0;
Address = Environment;
ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&Address,&Size,MEM_RELEASE);
Size = 0;
Address = ProcessParameters;
ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID *)&Address,&Size,MEM_RELEASE);
InitializationPhase += 1;
#if defined(_X86_)
KiInitializeInterruptTimers();
#endif
ExFreePool(ProcessInformation);
}
阶段1 初始化完成以后,内核已经完全初始化,执行体的各个组件进入正常运行状态。但作为一个操作系统,Windows 的引导过程尚未完成,仅仅内核正常工作还不够,系统必须让应用程序也能够运行起来。接下来的引导工作由刚刚启动起来的smss 进程继续进行。