1. 课程导入

Xen 是 Type-1 裸机型 Hypervisor 的代表实现,它在硬件与操作系统之间插入一层极薄的虚拟化抽象,使多个 Guest OS 能够以接近原生的性能共享同一颗 SoC。在 x86 服务器领域,Xen 的移植与启动路径已经相当成熟;而到了 ARM 平台,由于缺少统一的固件接口、设备树描述方式各异、启动异常向量与特权级模型完全不同,移植工作呈现出鲜明的“平台工程”特征。本课程的目标,就是把这套从源码到板级点亮、从 Hypervisor 启动到 Domain 0 引导的完整链路拆解清楚。

一、学习目标:从“能编译”到“能启动、能调试”

本课程不满足于让学员跑通一次编译,而是希望建立三层能力。第一层是结构认知:理解 Xen on ARM 的镜像组成、启动阶段划分、特权级切换与内存布局。第二层是工程能力:能够为一块具体开发板配置设备树、裁剪配置项、交叉编译并生成可引导镜像。第三层是排障能力:当串口只输出半行日志、当 Domain 0 卡在 earlycon、当 SMMU 报出 fault 时,能够依据启动时序定位问题环节。

学习完成后,你应当能够独立回答三个问题:Xen 在 ARM 上从哪一条指令开始执行?它如何把 CPU 交给 Domain 0?当启动失败时,第一个应该检查的日志和寄存器是什么?

二、核心概念:理解 Xen ARM 的四个关键词

  • EL2 与 HYP 模式:ARMv8 中 Xen 运行在 EL2,Guest OS 运行在 EL1,用户态在 EL0。EL2 是虚拟化的“控制层”,负责陷入捕获、阶段 2 地址翻译和中断路由。
  • Domain 0(Dom0):Xen 启动后创建的第一个特权虚拟机,承担驱动、工具栈与后续 Guest 的管理职责。没有 Dom0,Xen 只是一个空壳。
  • 设备树(Device Tree):ARM 平台没有 ACPI 那样的统一枚举机制,硬件拓扑、中断控制器、串口、内存区域都通过 DTB 描述。Xen 自身需要一份 DTB,Dom0 也需要一份(通常由 Xen 加工后传递)。
  • 启动链(Boot Chain):典型路径为 BootROM → BL1/BL2 → BL31(ATF)→ U-Boot → Xen → Dom0 Kernel → RootFS。每一级都可能成为移植的“卡点”。

三、整体框架:一张图看清启动信号链

下图给出 Xen ARM 平台从固件到 Dom0 的关键阶段与特权级迁移关系。它既是本课程的知识地图,也是后续排障时的对照基准:任何一次启动失败,都可以先判断“停在哪一级、当前处于哪个 EL、串口是否已经由 Xen 接管”。

Xen ARM 启动链:固件 → Hypervisor → Domain 0 BootROM / BL1 EL3 安全启动 BL31 (ATF) EL3 运行时固件 U-Boot EL2/EL1 引导加载 Xen Hypervisor EL2 接管 CPU Domain 0 Kernel EL1 特权虚拟机 Xen 启动内部阶段 head.S 入口 setup_mm start_xen create_dom0 Dom0 跳转 关键输入 • xen.dtb / 板级 DTS • Xen 配置项与内存布局 关键输出 • xen.gz / xen.efi 镜像 • 串口启动日志与 EL 状态 常见卡点 • DTB 不匹配 / 串口未接管 • Dom0 内存或中断冲突

四、方法流程:一条可复用的移植主线

面对一块新板子,建议按下面的顺序推进,而不是一上来就改代码。顺序本身就是一种排障策略:每一步都留下可验证的输出。

  1. 确认硬件与固件基线:确认 SoC 型号、ATF 版本、U-Boot 是否支持 EL2 启动、串口基地址与波特率。
  2. 准备设备树:从内核或厂商 BSP 中取板级 DTS,确认 hypervisor 节点、psci 节点、interrupt-controller 与 memory 描述完整。
  3. 配置与编译 Xen:选择 xen_defconfig 或板级 defconfig,设置交叉编译器与目标架构。
  4. 打包引导镜像:将 Xen、Dom0 内核、DTB、initrd 按 U-Boot 可识别的格式组织。
  5. 串口验证与逐级排障:先看 ATF/U-Boot 是否正常,再看 Xen 是否打印版本号,最后看 Dom0 是否进入用户态。
# 典型交叉编译与镜像准备(示意)
export CROSS_COMPILE=aarch64-linux-gnu-
make xen_defconfig
make -j$(nproc) xen
ls -lh xen/xen
# 生成镜像后,与 dom0 Image、xen.dtb 一起放入 boot 分区

五、案例场景:一块新开发板的首次点亮

假设你拿到一块基于 ARMv8 的开发板,厂商只提供了 U-Boot 和 Linux 内核,没有 Xen 支持。第一次启动时,串口可能只输出到 U-Boot 就停止,或者 Xen 打印了版本号却在 create_dom0 处挂起。此时不要盲目改代码,而应回到启动链:先确认 U-Boot 是否以 EL2 进入 Xen,再确认 Xen 是否成功解析 DTB 中的内存节点,最后确认 Dom0 的 bootargs 中 console= 与 earlycon 是否与真实串口一致。多数“首次点亮失败”都源于设备树与内存布局,而非 Xen 核心逻辑。

建议在移植初期打开 Xen 的早期串口输出(early printk),并保留 U-Boot 的 bootargs 日志。串口是 ARM 平台最可靠的“调试器”。

六、误区与复盘:新手最容易踩的四个坑

误区表现纠正思路
把 x86 经验直接套用找不到 ACPI、试图用 GRUB 引导ARM 依赖 DTB 与 U-Boot,启动链完全不同
忽略 EL 级别Xen 无法获得 HYP 权限,陷入失败确认 ATF/U-Boot 将 CPU 置于 EL2
DTB 一份走天下Dom0 看不到串口或内存不足Xen 与 Dom0 的 DTB 需分别校验
只看内核日志错过 Xen 早期输出,误判卡点从 BootROM 开始逐级确认串口输出
不要在没有串口输出的情况下反复烧写镜像。没有日志的移植等于盲调,先打通串口,再谈功能。

七、学习路径:本课程如何展开

后续章节将沿着“环境与工具 → 启动原理 → 设备树与配置 → 编译与镜像 → Dom0 引导 → 调试与优化”的主线推进。每一章都会围绕一个可验证的工程目标展开,并配有对应的命令、配置或日志片段。你在本章建立的整体框架,会在后续每一次排障中被反复调用:先定位阶段,再定位特权级,最后定位具体模块。

现在,请先记住这条主线:固件把 CPU 交给 Xen,Xen 在 EL2 建立虚拟化环境,再把 EL1 交给 Domain 0。理解这条链,就理解了 Xen ARM 移植与启动工程的全部起点。