FreeLDR → ntoskrnl.exe 启动过程分析

  1. 概述
    ReactOS 的启动加载器 FreeLDR (freeldr.sys) 负责将 Windows NT 兼容的内核 (ntoskrnl.exe) 和硬件抽象层 (hal.dll) 加载到内存中,建立合适的分页环境,最终将控制权转交给内核的入口点 KiSystemStartup。
    整个转换过程是裸机环境到 Windows 内核虚拟内存环境的完整过渡,完成了硬件初始化、文件解析、PE 镜像加载、内存布局重构、分页模式切换、内核权限移交等核心操作,完全兼容 Windows NT 内核启动规范,也是 ReactOS 实现 Windows 系统兼容启动的核心模块。
    整个转换过程可分为以下阶段:
    第一阶段: FreeLDR 初始化 (BootMain)
    第二阶段: 启动菜单与 OS 选择 (RunLoader)
    第三阶段: WinLDR 加载核心模块 (LoadWindowsCore)
    第四阶段: 注册表与硬件初始化
    第五阶段: 建立分页内存布局 (SetupMachineDependent)
    第六阶段: 切换到分页模式 (SetProcessorContext)
    第七阶段: 最终移交 (→ KiSystemStartup)

  1. 第一阶段: FreeLDR 自身初始化
    本阶段是整个启动流程的基石,运行于 CPU 保护模式、物理地址寻址环境下,核心目标是完成加载器自身环境搭建、基础硬件与软件模块初始化,为后续文件读取、镜像加载、内存管理提供基础运行环境,全程无虚拟内存、无分页机制。
    2.1 入口点
    [arch/i386/entry.S](file:///d:/reactos/boot/freeldr/freeldr/arch/i386/entry.S) — _RealEntryPoint
    该函数是 FreeLDR 最底层汇编入口,由 BIOS/VBR/MBR 引导程序跳转执行,是系统上电后内核运行前的核心过渡代码,核心执行逻辑如下:
  • 模式切换:从 BIOS 默认的 16 位实模式切换为 32 位保护模式,解锁 4GB 内存寻址能力,突破实模式 1MB 内存寻址限制。

  • 段寄存器初始化:统一设置数据段、代码段、栈段选择子为 PMODE_DS,构建平坦内存模型,消除段式内存寻址的地址偏移复杂度。

  • 栈初始化:初始化 32 位保护模式专属栈 stack32,为 C 语言代码运行提供栈空间,保障函数调用、局部变量存储正常执行。

  • 中断表初始化:加载临时 IDT(中断描述符表),初始化基础中断处理机制,避免启动过程中硬件中断触发导致系统异常。

  • 跳转 C 入口:完成底层硬件模式配置后,跳转至 C 语言入口函数,进入高层逻辑初始化阶段。
    2.2 C 入口
    [freeldr.c](file:///d:/reactos/boot/freeldr/freeldr/freeldr.c) — BootMain(CmdLine)
    BootMain 是 FreeLDR 高层初始化核心函数,按固定顺序完成所有基础组件初始化,各步骤具备强依赖关系,不可颠倒,核心代码与逻辑解析如下:
    VOID __cdecl BootMain(IN PCCH CmdLine)
    {
    LoadSettings(CmdLine); // 从命令行加载默认设置
    DebugInit(…); // 调试器预初始化
    MachInit(CmdLine); // 硬件平台初始化
    FrLdrCheckCpuCompatibility(); // 检查 CPU 兼容性
    UiInitialize(FALSE); // UI 预初始化
    MmInitializeMemoryManager(); // 初始化内存管理器
    FsInit(); // 初始化文件系统 I/O
    PeLdrInitializeModuleList(); // 初始化模块列表
    MachInitializeBootDevices(); // 初始化启动设备

    LaunchSecondStageLoader(); // 启动二级加载器 (rosload.exe)
    // 失败则 Reboot()
    }
    各函数核心作用详解:

  • LoadSettings:解析引导传递的命令行参数,加载默认启动配置,覆盖默认参数优先级低于后续 ini 配置与手动启动选项。

  • DebugInit:初始化串口、调试端口,开启启动日志输出,为启动故障排查提供调试支撑,未开启调试模式时为空实现。

  • MachInit:初始化 x86 平台基础硬件架构,识别主板、总线基础信息,完成平台通用硬件适配。

  • FrLdrCheckCpuCompatibility:检测 CPU 架构、指令集(32位、保护模式支持),不兼容则直接重启设备,保障启动基础环境合规。

  • UiInitialize(FALSE):轻量 UI 初始化,仅初始化图形基础资源,不加载完整菜单界面,为后续菜单显示做预热。

  • MmInitializeMemoryManager:初始化物理内存管理器,扫描 BIOS 内存映射,划分可用内存、保留内存、坏内存区域,为后续内存分配奠定基础。

  • FsInit:初始化 FAT32、NTFS 等文件系统驱动,注册文件读写接口,实现系统分区文件读取能力。

  • PeLdrInitializeModuleList:初始化内核模块链表,用于后续记录所有加载的内核、驱动、DLL 模块信息。

  • MachInitializeBootDevices:初始化磁盘、分区等启动设备,识别系统分区位置,建立设备访问通路。
    2.3 二级加载器 (rosload.exe)
    LaunchSecondStageLoader() 加载同目录或 loader/ 子目录下的 rosload.exe,该模块是 FreeLDR 的二级过渡加载器,核心设计为兼容适配 SCSI 启动驱动。
    rosload.exe 以 “scsiport.sys” 别名对外导出 ScsiPort* 系列函数,适配 ntbootdd.sys SCSI 启动驱动的依赖需求,解决传统 SCSI 磁盘启动的兼容性问题。加载完成后调用其入口点,由 rosload 完成完整版 freeldr.sys 内核模块的重加载,执行完毕后返回主加载器流程。
    实际启动路径(简化):
    BIOS → bootsect.bin → freeldr.sys → rosload.exe → 返回 freeldr.sys
    注:当不使用 SCSI 驱动、仅使用 IDE/SATA 普通磁盘启动时,rosload 无实际工作需求,会直接快速返回控制权给 freeldr.sys,跳过完整二级加载阶段,提升启动速度。


  1. 第二阶段: 启动菜单与操作系统选择
    本阶段核心是读取启动配置、展示启动菜单、接收用户选择、匹配对应系统启动方案,是人机交互与启动分支选择的核心阶段,运行于物理地址模式。
    [bootmgr.c](file:///d:/reactos/boot/freeldr/freeldr/bootmgr.c) — RunLoader()
    VOID RunLoader(VOID)
    {
    LoadBootDeviceDriver(); // 加载额外的 SCSI 启动设备驱动
    IniFileInitialize(); // 打开 FREELDR.INI
    UiInitialize(TRUE); // 初始化 UI(全功能)

    OperatingSystemList = InitOperatingSystemList(…); // 读取启动项列表

    // 显示启动菜单循环
    for (;😉
    {
    UiDisplayMenu(…); // 显示菜单
    LoadOperatingSystem(&…); // 加载所选操作系统
    }
    }
    核心流程解析:

  • LoadBootDeviceDriver:补充加载大容量磁盘、SCSI、RAID 等非常规启动设备驱动,确保系统分区可正常访问。
  • IniFileInitialize:解析根目录下FREELDR.INI 配置文件,读取启动超时、默认启动项、启动参数、菜单样式等配置。
  • UiInitialize(TRUE):初始化完整 UI 界面,加载字体、颜色、菜单渲染资源,支持键盘交互、超时自动选择功能。
  • InitOperatingSystemList:从 ini 配置中解析所有系统启动项,构建启动项链表,支持多系统共存启动。
  • 菜单循环:持续渲染启动菜单,监听键盘输入,超时未选择则自动选中默认启动项,调用加载函数执行启动流程。
    3.1 加载操作系统
    LoadOperatingSystem() 是启动分支分发核心函数,通过系统类型字符串匹配 OSLoadingMethods[] 注册表,调用对应的系统加载入口,实现不同版本 Windows/ReactOS 启动适配。
    static const OS_LOADING_METHOD OSLoadingMethods[] = {
    {“WindowsNT40” , …, LoadAndBootWindows},
    {“Windows” , …, LoadAndBootWindows}, // 即 Windows2003
    {“Windows2003” , …, LoadAndBootWindows},
    {“WindowsVista”, …, LoadAndBootWindows},
    {“ReactOSSetup”, …, LoadReactOSSetup},
    };
    其中 LoadAndBootWindows() 是兼容所有 NT 内核系统的 WinLDR 通用入口函数,后续所有内核加载、系统初始化逻辑均从该函数展开;LoadReactOSSetup 为系统安装模式专属启动入口,用于 ReactOS 安装流程初始化。

  1. 第三阶段: WinLDR 加载核心模块
    本阶段是内核启动的核心加载阶段,WinLDR 模拟 Windows 原生 NTLDR 加载逻辑,完成启动参数解析、核心数据结构初始化、注册表加载、NLS 数据初始化、内核及依赖镜像加载,是从配置解析到实体内核加载的关键过渡阶段。
    [winldr.c](file:///d:/reactos/boot/freeldr/freeldr/ntldr/winldr.c) — LoadAndBootWindows(Argc, Argv, Envp)
    4.1 解析启动参数
    从启动项参数数组Argv[] 中解析系统启动核心配置,所有参数将贯穿整个启动流程,并最终传递给内核,核心参数如下:
    参数
    示例
    说明
    SystemPartition
    multi(0)disk(0)rdisk(0)partition(1)
    系统分区 ARC 标准路径,内核设备识别核心标识
    SystemPath
    \ReactOS
    系统根目录路径,用于定位 system32 核心目录
    Options
    /FASTDETECT /MININT /DEBUG
    启动控制选项,控制硬件检测、调试模式、最小启动等特性
    BootType
    Windows2003
    操作系统版本类型,用于适配对应内核加载规则
    BootType 与 NT 内核版本严格映射,决定内核特性、兼容规则、内存模型,映射关系如下:
    BootType
    宏定义

    WindowsNT40
    _WIN32_WINNT_NT4
    0x0400
    Windows / Windows2003
    _WIN32_WINNT_WS03
    0x0502
    WindowsVista
    _WIN32_WINNT_VISTA
    0x0600
    4.2 分配并初始化 Loader Parameter Block
    AllocateAndInitLPB() 是启动流程核心函数,负责分配并初始化 LOADER_SYSTEM_BLOCK 结构体,该结构体是加载器与内核通信的唯一数据载体,内核启动的所有配置、内存信息、模块信息、硬件信息均通过该结构体传递。
    WinLdrSystemBlock = MmAllocateMemoryWithType(sizeof(LOADER_SYSTEM_BLOCK), LoaderSystemBlock);
    LoaderBlock = &WinLdrSystemBlock->LoaderBlock;

// 初始化三个关键双向链表
InitializeListHead(&LoaderBlock->LoadOrderListHead); // 模块加载顺序链表
InitializeListHead(&LoaderBlock->MemoryDescriptorListHead); // 内存描述符链表
InitializeListHead(&LoaderBlock->BootDriverListHead); // 启动驱动链表

// 设置 LoaderBlockExtension
Extension->MajorVersion = (Version >> 8) & 0xFF;
Extension->MinorVersion = Version & 0xFF;
Extension->BootViaEFI = …; // EFI 引导标记
核心链表作用:

  • LoadOrderListHead:记录所有内核、HAL、驱动、DLL 模块的加载顺序与内存地址,内核初始化时按该顺序初始化模块。
  • MemoryDescriptorListHead:记录整机内存布局、内存类型、可用范围,内核内存管理器基于该链表构建虚拟内存体系。
  • BootDriverListHead:记录所有启动阶段加载的核心驱动,内核 I/O 管理器优先加载该链表内驱动。
    4.3 加载 SYSTEM 配置单元
    注册表 SYSTEM 配置单元存储系统硬件配置、驱动加载规则、系统服务配置,是内核初始化的核心配置来源,WinLdrInitSystemHive() 负责完成注册表加载与解析。
    // 普通启动加载: SystemRoot\system32\config\SYSTEM
    // 安装模式加载: SystemRoot\SETUPREG.HIV

WinLdrLoadSystemHive(LoaderBlock, SearchPath, “SYSTEM”, &Reason);
-> ArcOpen(), ArcGetFileInformation(), ArcRead()
-> MmAllocateMemoryWithType(fileSize, LoaderRegistryData)

// 导入二进制配置单元到内存注册表
RegImportBinaryHive(VaToPa(LoaderBlock->RegistryBase), LoaderBlock->RegistryLength, …);

// 失败时回退到 SYSTEM.ALT
if (!Success) 尝试加载 “SYSTEM.ALT” 并标记 HBOOT_BOOT_RECOVERED_BY_ALTERNATE_HIVE

// 初始化 CurrentControlSet 链接
RegInitCurrentControlSet(FALSE);
异常容错机制:当默认 SYSTEM 注册表文件损坏、缺失时,自动加载备份文件 SYSTEM.ALT,保障系统可正常启动,同时标记启动恢复状态,供内核后续识别修复。最终初始化 CurrentControlSet 链接,指向当前生效的系统配置集。
4.4 扫描注册表与加载 NLS 数据
WinLdrScanSystemHive() 完成注册表驱动扫描、NLS 语言资源加载,为系统硬件适配、字符编码、语言显示提供支撑。
// 1. 扫描注册表,构建启动驱动列表
WinLdrScanRegistry(&LoaderBlock->BootDriverListHead);
-> CmpFindDrivers() // 从 HKLM\SYSTEM\CurrentControlSet\Services 查找启动驱动
-> CmpSortDriverList() // 按组/标签排序
-> CmpResolveDriverDependencies() // 解析循环依赖

// 2. 从注册表获取 NLS 文件名
WinLdrGetNLSNames(…, &AnsiFileName, &OemFileName, &LangFileName, …);
// 读取 HKLM…\Control\NLS\CodePage\ACP
// 读取 HKLM…\Control\NLS\CodePage\OEMCP
// 读取 HKLM…\Control\NLS\Language\Default

// 3. 加载 NLS 文件
WinLdrLoadNLSData(…, SearchPath, &AnsiFileName, &OemFileName, &LangFileName, …);
-> 加载 c_1252.nls (ANSI), c_437.nls (OEM), l_intl.nls (Unicode CaseTable)
-> 分配 LoaderNlsData 类型内存
核心逻辑说明:

  • 驱动扫描会自动解析驱动依赖关系、解决循环依赖,按系统预设优先级排序,避免驱动加载顺序错误导致硬件初始化失败。
  • NLS 数据决定系统默认编码、大小写转换规则、语言适配,是系统文本显示、文件编码识别的基础,加载后统一存入 LoaderBlock 供内核调用。
    4.5 进入 LoadAndBootWindowsCommon()
    该函数是启动流程的不可返回前置节点,完成所有兜底准备工作,后续流程一旦执行将无法回退,直至内核移交。
    // 1. 设置 EMS 重定向
    WinLdrSetupEms(BootOptions);

// 2. 硬件检测
LoaderBlock->ConfigurationRoot = MachHwDetect(BootOptions);
// 检测 PCI 总线、ACPI、显示设备等,构建 ARC 配置树

// 3. ★ 加载 Windows 内核核心 ★
LoadWindowsCore(OperatingSystemVersion, LoaderBlock, BootOptions, BootPath, &KernelDTE);
硬件检测阶段会遍历整机总线与设备,构建标准化 ARC 硬件配置树,记录所有可识别硬件设备信息,内核 HAL 层将基于该配置树完成硬件初始化。
4.6 LoadWindowsCore() — 加载内核/HAL/KD DLL
该函数是核心镜像加载入口,负责加载所有内核必备核心组件,解析启动选项适配不同内核加载模式,递归处理所有模块依赖。
LoadWindowsCore()
├── 解析启动选项: 3GB, SAFEBOOT, BOOTLOGO, NOEXECUTE, PAE 等
├── 确定 HAL 和 KERNEL 文件名 (默认 hal.dll, ntoskrnl.exe)
│ └── 支持 /HAL=xxx.dll 和 /KERNEL=xxx.exe 覆盖
├── ★ 加载内核 ntoskrnl.exe → LoaderSystemCode
│ └── PeLdrLoadImage(BootPath\system32\ntoskrnl.exe, LoaderSystemCode, &KernelBase)
├── ★ 加载 HAL hal.dll → LoaderHalCode
├── ★ 加载 KD 传输 DLL kdcom.dll → LoaderSystemCode
│ └── 根据 /DEBUGPORT= 选项选择: kdcom.dll / kd1394.dll / kdusb2.dll
├── ★ 加载 BootVid bootvid.dll → LoaderSystemCode
│ └── 自动检测: PC-98 → pc98bvid.dll, Xbox → xboxbvid.dll, EFI → lfbbvid.dll, 含 Framebuffer → lfbbvid.dll
├── 扫描并加载所有导入依赖 (PeLdrScanImportDescriptorTable)
│ └── 递归加载 kernel32.dll, nt.dll 等系统 DLL
└── 成功返回
PE 加载器核心处理流程:PeLdrLoadImage() 是自定义 PE 镜像加载器,完全适配 NT 内核镜像格式,核心步骤如下:

  1. 读取 DOS 头、PE 头,校验镜像合法性,拒绝损坏、不兼容的内核文件;
  2. 解析节表(代码节、数据节、资源节),按内存对齐规则规划内存布局;
  3. 将各节数据映射到对应类型的物理内存区域;
  4. 遍历重定位表,修正镜像内存地址,适配实际加载基址;
  5. 初始化安全 Cookie,开启内核基础安全防护机制。

  1. 第四阶段: 加载启动驱动与 Phase1 初始化
    本阶段聚焦启动驱动加载与核心数据结构完善,完成硬件驱动预加载、LoaderBlock 结构体补全、物理地址预处理,为分页模式切换和内核移交做最后数据准备。
    5.1 加载启动驱动
    基于前期注册表扫描生成的驱动链表,批量加载系统必备启动驱动,保障磁盘、总线、基础硬件在内核初始化前可用。
    WinLdrLoadBootDrivers(LoaderBlock, BootPath);
    // 遍历 BootDriverListHead
    // 对每个启动驱动:
    // PeLdrLoadImage(BootPath + FilePath, LoaderBootDriver, &DriverBase)
    // PeLdrAllocateDataTableEntry(…)
    // PeLdrScanImportDescriptorTable(…) // 加载依赖
    所有启动驱动统一加载至 LoaderBootDriver 类型内存,记录加载地址、大小、依赖关系,存入驱动链表,内核启动后直接复用,无需重复加载。
    5.2 WinLdrInitializePhase1()
    一阶初始化是启动流程的数据补全核心函数,完成所有路径信息、硬件信息、内存信息的填充,同时完成物理地址到虚拟地址的预处理转换,是分页切换的前置关键步骤。
    WinLdrInitializePhase1(OperatingSystemVersion, LoaderBlock, BootOptions, SystemPartition, BootPath);
    ├── 填充 LoaderBlock->ArcBootDeviceName (引导分区 ARC 路径)
    ├── 填充 LoaderBlock->ArcHalDeviceName (系统分区 ARC 路径)
    ├── 填充 LoaderBlock->NtBootPathName (系统根目录, 如 \ReactOS)
    ├── 填充 LoaderBlock->NtHalPathName (HAL 路径, 通常为 )
    ├── 填充 LoaderBlock->LoadOptions (启动选项字符串)
    ├── 填充 ARC 磁盘信息列表 ArcDiskInformation
    ├── 转换所有链表到 VA 地址 (PaToVa)
    │ └── List_PaToVa(&LoadOrderListHead)
    │ └── ConvertConfigToVA(ConfigurationRoot)
    │ └── List_PaToVa(&BootDriverListHead)
    ├── 设置 ACPI 表指针
    ├── 填充 LoaderPerformanceData (Vista+)
    ├── 填充 HeadlessLoaderBlock (EMS 远程管理)
    └── 加载驱动数据库 drvmain.sdb (LoaderRegistryData)
    核心价值:统一所有核心数据结构的地址类型,解决分页切换后物理地址失效问题,确保内核接收的所有数据均为合法虚拟地址,避免内核初始化指针异常。

  1. 第五阶段: 建立分页内存布局
    本阶段是物理内存寻址到虚拟内存分页寻址的核心铺垫阶段,完成硬件收尾、架构相关内存配置、页目录/页表分配、特殊内存映射、内存描述符构建,搭建 Windows 内核标准的 32 位分页内存模型。
    6.1 MachPrepareForReactOS()
    分页切换前的硬件收尾函数,停止所有闲置硬件设备(软驱马达、闲置总线),重置显示模式为内核默认显示状态,清理启动阶段残留的硬件状态,避免硬件占用内存导致分页映射冲突,保障内存布局纯净。
    6.2 WinLdrSetupMachineDependent() — 架构相关设置
    [arch/i386/winldr.c](file:///d:/reactos/boot/freeldr/freeldr/arch/i386/winldr.c),为 x86 架构专属初始化函数,完成 PCR、TSS、GDT/IDT、页表、特殊内核区域的内存分配与映射。
    WinLdrSetupMachineDependent(LoaderBlock)
    {
    LoaderBlock->u.I386.CommonDataArea = NULL; // 禁止 ABIOS
    LoaderBlock->u.I386.MachineType = MACHINE_TYPE_ISA;

    // 1. 分配 PCR (处理器控制区) 页面 — 2 页
    Pcr = MmAllocateMemoryWithType(2 * MM_PAGE_SIZE, LoaderStartupPcrPage);
    // 第 1 页: Boot Processor PCR (KIP0PCRADDRESS = 0xFFDFF000)
    // 第 2 页: KI_USER_SHARED_DATA (0xFFDF0000)

    // 2. 分配 TSS (任务状态段)
    Tss = MmAllocateMemoryWithType(TssSize, LoaderMemoryData);

    // 3. 分配 GDT + IDT 空间 (128 GDT 条目 + 256 IDT 条目)
    GdtIdt = MmAllocateMemoryWithType(NumPages * MM_PAGE_SIZE, LoaderMemoryData);

    // 4. ★ 分配页目录和页表
    MempAllocatePageTables();
    // 计算所需页表数: NumPageTables = 总物理页数 >> 10
    // 总大小 = PDE(1) + HAL页表(1) + 物理PT(NumPageTables) + 内核PT(NumPageTables)
    // PDE 自映射在 0xC0300000 (SELFMAP_ENTRY = 0x300 即 PDE[768])
    // HAL 页表映射在 PDE[1023] (虚拟地址 0xFFC00000-0xFFFFFFFF)

    // 5. 映射特殊页面
    WinLdrMapSpecialPages();
    // KI_USER_SHARED_DATA → Pcr+1 页
    // KIP0PCRADDRESS → Pcr 页
    // APIC 寄存器 → HAL 页表区域
    }
    x86 标准分页结构布局:
    CR3 → PDE[0…1023] (自映射在 0xC0300000)
    ├── PDE[0] → PhysicalPT[0] (标识映射低端内存)
    ├── …
    ├── PDE[0x300] → 自身 (自映射)
    ├── …
    ├── PDE[768] → KernelPT[0] (KSEG0_BASE = 0x80000000)
    ├── …
    └── PDE[1023] → HalPageTable (0xFFC00000-0xFFFFFFFF)
    ├── APIC 映射
    ├── PCR (0xFFDFF000)
    └── KI_USER_SHARED_DATA (0xFFDF0000)
    该布局完全兼容 Windows x86 内核内存规范,实现用户空间、内核空间、HAL 专属空间、自映射空间的严格划分。
    6.3 WinLdrSetupMemoryLayout() — 创建内存描述符
    [wlmemory.c](file:///d:/reactos/boot/freeldr/freeldr/ntldr/wlmemory.c),负责梳理整机内存资源,标准化划分内存类型,构建内核可识别的内存描述符链表。
    WinLdrSetupMemoryLayout(LoaderBlock)
    {
    FrLdrHeapCleanupAll(); // 清理堆

    // 1. 分配 Memory Allocation Descriptors (MAD)
    Mad = MmAllocateMemoryWithType(200 * sizeof(MEMORY_ALLOCATION_DESCRIPTOR), LoaderMemoryData);

    // 2. 遍历内存查找表,为每个区域设置分页
    MemoryMap = MmGetMemoryMap(&NoEntries);
    for (i = 2; i < NoEntries; i++)
    {
    MempSetupPagingForRegion(LastPageIndex, i - LastPageIndex, LastPageType);
    // LoaderSystemCode / LoaderHalCode → 内核模式映射
    // LoaderFree / LoaderBad → 不映射
    // LoaderFirmwareTemporary → 用户模式映射
    }

    // 3. 创建内存描述符链表 (插入排序 + 合并空闲块)
    for (i = 0; i < NoEntries; i++)
    MempAddMemoryBlock(LoaderBlock, BasePage, PageCount, Type);

    // 4. 添加 BIOS 内存映射中的高端描述符
    for (i = 0; i < BiosMemoryMapEntryCount; i++)
    MempAddMemoryBlock(LoaderBlock, …);

    // 5. 转换内存描述符链表到 VA
    List_PaToVa(&LoaderBlock->MemoryDescriptorListHead);
    }
    内存类型映射规则(内核内存权限管控核心):
    内存类型
    区域用途
    映射模式
    LoaderSystemCode
    内核代码
    内核模式
    LoaderHalCode
    HAL 代码
    内核模式
    LoaderRegistryData
    注册表配置单元
    内核模式
    LoaderNlsData
    NLS 表
    内核模式
    LoaderStartupPcrPage
    PCR 页
    内核模式
    LoaderBootDriver
    启动驱动
    内核模式
    LoaderLoadedProgram
    加载的程序
    用户模式
    LoaderFirmwareTemporary
    固件临时
    用户模式
    LoaderOsloaderStack
    加载器栈
    用户模式
    LoaderFree
    空闲内存
    不映射
    LoaderBad
    坏内存
    不映射
    LoaderSpecialMemory
    特殊内存 (MMIO)
    不可见


  1. 第六阶段: 切换到分页模式
    本阶段是整个启动流程最核心、最关键的硬件模式切换步骤,完成从平坦物理地址模式到标准 NT 内核分页虚拟地址模式的切换,重构 CPU 寻址方式、段描述符、中断机制,让系统完全进入内核运行环境。
    [arch/i386/winldr.c](file:///d:/reactos/boot/freeldr/freeldr/arch/i386/winldr.c) — WinLdrSetProcessorContext()
    7.1 执行步骤
    WinLdrSetProcessorContext(OperatingSystemVersion)
    {
    Pcr = KIP0PCRADDRESS; // 0xFFDFF000
    Tss = KSEG0_BASE | (TssBasePage << 12); // 内核虚拟地址空间的 TSS

    // 1. 关闭中断
    _disable();

    // 2. 重置 EFLAGS
    __writeeflags(0);

    // 3. 临时关闭分页 (UEFI 可能已经启用 PAE 分页)
    __writecr0(__readcr0() & ~CR0_PG);
    __writecr4(__readcr4() & ~CR4_PAE);

    // 4. ★ 设置新的页目录基址 (PDBR)
    __writecr3((ULONG_PTR)PDE);

    // 5. 启用分页
    __writecr0(__readcr0() | CR0_PG);

    // 从这里开始,CPU 在分页模式下运行!
    // FreeLDR 仍然可以通过标识映射 + 内核映射访问

    // 6. 清零 PCR 页 (内核期望空 PCR)
    RtlZeroMemory((PVOID)Pcr, MM_PAGE_SIZE);

    // 7. ★ 构建新的 GDT 和 IDT
    GDT 布局:
    KGDT_NULL (0x00): Null 选择子
    KGDT_R0_CODE (0x08): Ring 0 代码段 (平坦 4GB)
    KGDT_R0_DATA (0x10): Ring 0 数据段 (平坦 4GB)
    KGDT_R3_CODE (0x18): Ring 3 代码段 (平坦 4GB)
    KGDT_R3_DATA (0x20): Ring 3 数据段 (平坦 4GB)
    KGDT_TSS (0x28): TSS 段 (Ring 0)
    KGDT_R0_PCR (0x30): PCR 段 (Ring 0) — 基址 = KIP0PCRADDRESS
    KGDT_R3_TEB (0x38): TEB 段 (Ring 3)
    KGDT_VDM_TILE(0x40): VDM BIOS 数据段 (Ring 3)
    KGDT_LDT (0x48): LDT (保留)
    KGDT_DF_TSS (0x50): 双错 TSS
    KGDT_NMI_TSS (0x58): NMI TSS
    0x60: 保留 (Vista+)
    0x68: 视频显示缓冲 (Vista+ 保留)
    0x70: GDT 别名 (Vista+ 基址不固定)

    IDT: 复制旧的 IDT 内容到新位置

    // 8. 加载新 GDT + IDT
    Ke386SetGlobalDescriptorTable(&GdtDesc);
    __lidt(&IdtDesc);

    // 9. 远跳转刷新 CS 预取队列
    asm(“ljmp $0x08, $1f\n 1:\n”); // 跳转到 KGDT_R0_CODE

    // 10. 设置段寄存器
    SS = KGDT_R0_DATA
    DS = KGDT_R0_DATA
    ES = KGDT_R0_DATA // 对 rep stosd 至关重要!
    FS = KGDT_R0_PCR // FS 指向 PCR → 内核通过 FS 访问 KPCR
    GS = 0
    LDT = 0

    // 11. 加载 TSR (任务寄存器)
    Ke386SetTr(KGDT_TSS);
    }
    关键核心细节解析:

  • 中断关闭:模式切换全程关闭中断,避免硬件中断打断分页配置,导致内存映射异常、系统崩溃。
  • 分页重置与重建:临时关闭原有分页机制,清空残留配置,加载全新标准化页目录,保证分页环境纯净合规。
  • 远跳转刷新:通过远跳转刷新 CPU 代码段预取队列,清除旧模式下的指令缓存,确保后续代码在全新段机制下运行。
  • FS 寄存器绑定:将 FS 寄存器固定指向 PCR 区域,是 Windows 内核核心特性,内核所有线程、CPU 核心信息均通过 FS 寄存器快速访问。
    7.2 地址空间转换
    分页启用后,CPU 完全基于虚拟地址寻址,构建用户/内核隔离的完整地址空间,映射规则完全匹配 Windows NT 规范:
    地址范围
    映射目标
    说明
    0x00000000 - 0x7FFFFFFF
    用户模式
    标识映射的低端物理内存,供应用层使用
    0x80000000 - 0x9FFFFFFF
    内核模式
    KSEG0_BASE 映射区域,存放内核、HAL、驱动核心代码
    0xC0000000 - 0xC00FFFFF
    自映射
    页目录自映射区域,用于动态修改页表配置
    0xFFC00000 - 0xFFFFFFFF
    HAL 区域
    存放页表、PCR、系统共享数据、APIC 映射
    核心特性:已加载的内核、FreeLDR 镜像同时存在于物理标识映射和内核虚拟映射区域,保障模式切换前后数据无缝衔接,内核可直接通过虚拟地址访问所有启动阶段加载的资源。

  1. 第七阶段: 最终移交给内核
    本阶段是启动加载器的收尾阶段,完成所有数据校验、地址转换、环境初始化,最终将 CPU 控制权完整移交 ntoskrnl.exe 内核,加载器生命周期结束,系统进入内核初始化阶段。
    8.1 移交前的最后准备
    在 LoadAndBootWindowsCommon() 函数尾部,完成最终环境校验与数据补全:
    // 1. 保存 LoaderPagesSpanned (以供内核使用)
    LoaderBlock->Extension->LoaderPagesSpanned = MmGetLoaderPagesSpanned();

// 2. 清零 KI_USER_SHARED_DATA 页面
RtlZeroMemory((PVOID)KI_USER_SHARED_DATA, MM_PAGE_SIZE);

// 3. 调试输出内存描述符、启动驱动、ARC 磁盘信息

// 4. 获取内核入口点
KiSystemStartup = (KERNEL_ENTRY_POINT)KernelDTE->EntryPoint;

// 5. 将 LoaderBlock 转换为虚拟地址
LoaderBlockVA = PaToVa(LoaderBlock);
所有准备工作确保内核接收的入口地址、数据结构体、内存环境完全合法,无残留脏数据、无地址错误。
8.2 最终移交
// 传递控制权给内核!
(*KiSystemStartup)(LoaderBlockVA);

// 永远不会返回到这里
UNREACHABLE;
该调用是不可逆的控制权移交,调用后 CPU 跳转至内核入口函数,FreeLDR 加载器彻底退出运行,后续所有系统行为均由内核主导。
8.3 内核初始化的开始
KiSystemStartup(LoaderBlockVA) 作为 ntoskrnl.exe 唯一入口,启动内核全模块初始化,逐层完成系统核心组件加载:
KiSystemStartup(LoaderBlock)
├── 从 LoaderBlock 获取内存描述符列表
├── 建立内核自己的分页结构
├── 初始化 PCR (Processor Control Region) 和 PRCB
├── 初始化 HAL (HalInitSystem)
├── 初始化内存管理器 (MmInitSystem)
│ └── 创建非分页池和分页池
├── 初始化对象管理器 (ObInitSystem)
├── 初始化进程/线程管理器 (PsInitSystem)
├── 初始化配置管理器 (CmInitSystem) — 注册表
├── 初始化 I/O 管理器 (IoInitSystem)
│ └── 加载启动驱动
├── 初始化安全引用监视器 (SeInitSystem)
├── …
└── → 最终创建 System 进程和 SMSS → Win32 子系统 → GUI → 登录


  1. 关键数据结构
    以下两个结构体是加载器与内核交互的核心载体,承载所有启动信息,是系统启动的核心数据基石。
    9.1 LOADER_PARAMETER_BLOCK
    核心参数块,存储模块、内存、驱动、设备、启动配置等所有核心信息:
    typedef struct _LOADER_PARAMETER_BLOCK {
    LIST_ENTRY LoadOrderListHead;
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐