跳转至

wimboot 架构

wimboot 是一个引导加载程序,大致类似于 Windows 的 pxeboot 网络启动程序。它让你通过 HTTP 或任何其他支持的协议获取所有相关文件,并把执行权交给 Windows 启动管理器 bootmgr.exe。本页详细介绍了 wimboot 架构、bootmgr.exe 操作概述以及故障排除技巧。

wimboot 的工作原理

wimboot 执行时,它会先设置引导加载程序环境,然后把执行权交给 Windows 启动管理器 bootmgr.exe,后者根据提供的启动配置数据(BCD)继续启动 Windows。

命令行处理

wimboot 运行时,首先处理提供给它的命令行参数。以下参数可用于更改其行为:

  • rawbcd:禁用在 BCD 文件中对 .exe.efi 的重写。
  • gui:显示图形启动消息。
  • pause[=quiet]:显示信息并等待按键。
  • index=n:使用 WIM 镜像索引 n
  • initrdfile:忽略。用于 syslinux 兼容性。

CPIO 归档提取

与 Linux 内核非常相似,wimboot 会提取 iPXE 提供给它的 CPIO 归档。CPIO 归档可以通过 imgfetch 命令用 iPXE 构造,每次调用的形式应为:

  imgfetch --name <somename> <uri> <somename>

这将获取由 <uri> 给出的文件,并使其在提供给 wimboot 的 CPIO 归档中以虚拟文件名 <somename> 可用。可以连续使用多个 imgfetch 命令。看似冗余的 <somename> 语句用于同时提供对 UEFI 模式(--name <name>)和传统 BIOS 模式(<name> 作为第一个参数)的兼容性。

注意: 虚拟文件名 <somename> 必须是不带子目录部分的扁平文件名。

wimboot 虚拟文件系统

来自 CPIO 归档的文件在 wimboot 提供的虚拟文件系统中可用。以下是这在实践中的工作示例:

  imgfetch --name BCD http://.../test.bcd BCD
  imgfetch --name boot.sdi http://.../boot.sdi boot.sdi
  imgfetch --name boot.wim http://.../install.wim boot.wim

这会获取三个文件,当 wimboot 执行时,它们都将在虚拟文件系统的多个位置可用。这些位置如下:

  • \
  • \Boot
  • \Boot\Fonts
  • \Boot\Resources
  • \Sources
  • \EFI
  • \EFI\Boot
  • \EFI\Microsoft
  • \EFI\Microsoft\Boot

因此对于任何给定的文件,比如 test.bcd,它将在上述所有位置以文件名 BCD 可用:

  • \BCD
  • \Boot\BCD
  • \Boot\Fonts\BCD
  • \Boot\Resources\BCD
  • \Sources\BCD
  • \EFI\BCD
  • \EFI\Boot\BCD
  • \EFI\Microsoft\BCD
  • \EFI\Microsoft\Boot\BCD

在实践中,这使得 bootmgr.exe 能够读取 \Boot\BCD 和相关文件,即使它们看似只被 imgfetch 命令放在根目录中。这可以避免修改 BCD 文件。

设置完成后,虚拟文件系统随后被映射到一个磁盘驱动器,以便 ''bootmgr.exe'' 能够识别并从中读取文件。

注意: BCD 文件名必须就是 BCD,这样才能被 bootmgr.exe 找到;它只搜索特定路径 \Boot\BCD

注意: boot.sdiboot.wim 路径必须与 BCD 文件中引用的路径匹配。换句话说,BCD 可以引用 boot.wim\Boot\boot.wim\Sources\boot.wim 或任何其他可识别的子目录;它们都可以工作。另一方面,如果 BCD 引用的路径不在虚拟文件系统覆盖范围内,比如 \Image\boot.wim,则会失败。

提取 bootmgr.exe

wimboot 从 CPIO 归档中读取每个文件时,文件的虚拟文件名会与一个特殊名称列表进行匹配,以查看是否需要特殊处理:* ''bootmgr.exe'':将其设置为在引导加载程序初始化结束时要执行的文件。* ''bootmgr'':尝试从这个压缩的 bootmgr 文件中提取 ''bootmgr.exe'',除非已经提供了 bootmgr.exe

  • *.wim:尝试从提供的 WIM 镜像内的静态路径 \Windows\Boot\PXE\bootmgr.exe 提取 bootmgr.exe,除非已经提供了 bootmgr.exe

注意: 从 WIM 镜像中提取 bootmgr.exe 的能力是在 wimboot v2.2.0 中添加的。这是一个非常方便的特性,让你不必显式提供 bootmgr.exe 文件。

注意: 如果没有找到 bootmgr.exewimboot 会退出。

运行 bootmgr.exe

wimboot 执行的最后阶段,它把 bootmgr.exe 读入内存并把执行权交给它。

EFI 平台差异

在 EFI 平台上,一些细节有所变化:

  • wimboot 可以使用路径 \Windows\Boot\EFI\bootmgfw.efi 从提供的 WIM 镜像中提取 bootmgfw.efi
  • wimboot 会自动修补 BCD 文件,把任何出现的 .exe 更改为 .efi。此行为可以通过 wimbootrawbcd 参数禁用。

bootmgr.exe 的工作原理

bootmgr.exe 执行时,wimboot 已经为它设置好了第一阶段环境,其中包含 wimboot 建立的虚拟文件系统。运行时,它执行以下步骤。

一些更多细节请参阅 Microsoft 的 How Booting into a Boot Image Works 文档。

读取 BCD 文件

bootmgr.exe 首先与虚拟文件系统交互,尝试从硬编码路径 \Boot\BCD 读取 BCD(启动配置数据存储)文件。

BCD 文件可以来自各种来源,也可以使用 Windows 中的 bcdedit.exe 工具从头创建或修改。更多信息请参见故障排除部分。

一些更多细节请参阅 Microsoft 的 Boot Process and BCDEdit 文档。

读取其他文件

读取 BCD 后,bootmgr.exe 将尝试从虚拟文件系统读取 BCD 引用的文件,例如存根内存盘 SDI(系统部署镜像)路径,如 \Boot\boot.sdi,以及主 WIM(Windows 镜像格式)镜像,如 \Boot\boot.wim

注意: SDI 镜像通常总是由各种 Windows 版本和部署工具以 boot.sdi 的形式提供,它是一个没有内容的模板内存盘。它不会有重大变化,也不需要对其进行任何更改或定制。

注意: WIM 镜像是主镜像,要么从 Windows 安装介质中提取,要么包含由用户或某些自动化镜像创建/捕获过程创建的自定义内容。

挂载内存盘

从虚拟文件系统读取相关文件后,bootmgr.exe 挂载空白模板 SDI 内存盘,并把 WIM 镜像附加到其中。

执行 winload.exe

SDI+WIM 内存盘挂载就绪后,bootmgr.exe 把执行权交给内存盘,就像它是一个实际磁盘一样。加载例程最终将执行权交给 \Windows\system32\winload.exe

故障排除

启动过程中可能会出现多种问题,而 bootmgr.exe 的错误处理和输出相当含糊,使人很难弄清楚确切的问题所在。

错误示例

下面列出了一些常见的错误消息:

  • bootmgr.exe 找不到或无法读取 BCD 文件时:
  File: \Boot\BCD
  Status: 0xc000000f
  Info: An error occurred while attempting to read the boot configuration data.
  • bootmgr.exe 找不到或无法读取 SDI 文件时:
  Status: 0xc000000f
  Info: The boot selection failed because a required device is inaccessible.
  • bootmgr.exe 找不到或无法读取 WIM 镜像时:
  Status: 0xc000000f
  Info: A required device isn't connected or can't be accessed.

找出罪魁祸首

通常的罪魁祸首是 BCD 文件中的不正确数据,这会导致 bootmgr.exe 停滞,不知道如何继续。不正确的数据通常可能是:

  • 引用了错误的文件名;或者
  • 引用了 wimboot 虚拟文件系统所提供目录之外的文件,例如 \\Boot\Sources 等之外的目录。

通过遵循 wimboot/bootmgr.exe 执行链,故障排除会容易一些:

  1. 确保使用最新的 wimboot 版本。
  2. 确保 imgfetch 命令成功。
    • 使用 imgstat 命令(在脚本中可选地后跟 prompt)来验证 imgfetch 获取的文件已成功检索。
  3. 使用 wimbootpause 参数,在 wimboot 打印出有关其初始化的一些有用信息后立即暂停执行:
  imgexec wimboot pause

下面是 imgstat 的示例输出,显示了检索到的文件、它们的大小和参数。这些参数将映射到 wimboot 建立的虚拟文件系统中可用的文件名:

  test.bcd : 12288 bytes "BCD"
  boot.sdi : 3170304 bytes "boot.sdi"
  install.wim : 404876823 bytes "boot.wim"
  wimboot : 34576 bytes [bzImage] [SELECTED] "pause=quiet"

wimboot 暂停屏幕的示例输出:

  wimboot v2.4.1 -- Windows Imaging Format bootloader -- http://ipxe.org/wimboot

  Command line: "pause=quiet"
  Using BCD via 0x67925074 len 0x3000
  Using boot.sdi via 0x67929078 len 0x306000
  Using boot.wim via 0x67c30078 len 0x1821ee17
  ...found WIM file boot.wim
  ...found file "\Windows\Boot\PXE\bootmgr.exe"
  Using bootmgr.exe via 0x2bc18 len 0x7fd80
  ...extracted bootmgr.exe
  Emulating drive 0x81
  Entering bootmgr.exe with parameters at 0x2a520

至于 bootmgr.exe,它将尝试从 \Boot\BCD 读取必需的 BCD。一旦成功,它将检查 BCD 并相应地采取行动。确保 BCD 包含有效数据,并引用 wimboot 虚拟文件系统内可到达的文件和路径。

BCD 内容

确保 BCD 文件包含有效的配置。为此,你需要使用仅限 Windows 的 bcdedit.exe 程序检查它。或者,可以在其他平台上使用 hivex 注册表配置单元库和相关的语言绑定,但 BCD 配置单元的结构和数据编码使其非常繁琐。

这是一个使用 ''bcdedit.exe'' 转储 BCD 内容的示例:

  > bcdedit /enum all /store test.bcd

  Windows Boot Manager
  --------------------
  identifier        {bootmgr}
  description       Windows 7 Boot Manager
  displayorder      {9b0a74...}
  timeout           30

  Windows Boot Loader
  -------------------
  identifier        {9b0a74...}
  device            ramdisk=[boot]\Boot\boot.wim,{ramdiskoptions}
  description       Windows 7 Installation
  osdevice          ramdisk=[boot]\Boot\boot.wim,{ramdiskoptions}
  systemroot        \Windows
  detecthal         Yes
  winpe             Yes

  Setup Ramdisk Options
  ---------------------
  identifier        {ramdiskoptions}
  description       Ramdisk Options
  ramdisksdidevice  boot
  ramdisksdipath    \Boot\boot.sdi

你可能会发现 BCD 文件包含对路径永远不会匹配 wimboot 搜索列表(''\''、''\Boot''、''\Sources'' 等)的文件的引用。如果是这种情况,你可能需要编辑或重新创建 BCD 文件。

这是一个从头创建 BCD 文件的示例:

  set BCDSTORE=BCD
  bcdedit /createstore %BCDSTORE%
  bcdedit /store %BCDSTORE% /create {ramdiskoptions} /d "Ramdisk Options"
  bcdedit /store %BCDSTORE% /set {ramdiskoptions} ramdisksdidevice Boot
  bcdedit /store %BCDSTORE% /set {ramdiskoptions} ramdisksdipath \Boot\boot.sdi
  for /f "Tokens=3" %%x in ('bcdedit /store %BCDSTORE% /create /d "Windows 7 Installation" /application osloader') do set GUID=%%x
  bcdedit /store %BCDSTORE% /set %GUID% systemroot \Windows
  bcdedit /store %BCDSTORE% /set %GUID% detecthal Yes
  bcdedit /store %BCDSTORE% /set %GUID% winpe Yes
  bcdedit /store %BCDSTORE% /set %GUID% osdevice ramdisk=[boot]\Boot\boot.wim,{ramdiskoptions}
  bcdedit /store %BCDSTORE% /set %GUID% device ramdisk=[boot]\Boot\boot.wim,{ramdiskoptions}
  bcdedit /store %BCDSTORE% /create {bootmgr} /d "Windows 7 Boot Manager"
  bcdedit /store %BCDSTORE% /set {bootmgr} timeout 30
  bcdedit /store %BCDSTORE% /set {bootmgr} displayorder %GUID%
  bcdedit /enum all /store %BCDSTORE%

BCD 是一个复杂的巨兽,有大量选项,这使得很难知道特定类型的启动(ADK、AIK、WDS 等)需要哪些细节。更多细节请参阅 iPXE WinPE 文档。

WIM 镜像内容

WIM 文件可以使用各种工具打开。如果你需要在无法访问 Windows 的情况下验证 WIM 镜像的内容,请使用 wimlib 提供的示例应用程序 applywim。这对于验证 bootmgr.exe 确实位于 \Windows\Boot\PXE\ 目录中很有用。