Zephyr 在 ACRN 虚拟化环境中的运行实战:预启动 VM 构建、XML 配置与 EFI 引导完整指南

Zephyr 在 ACRN 虚拟化环境中的运行实战:预启动 VM 构建、XML 配置与 EFI 引导完整指南

【免费下载链接】zephyr Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures. 【免费下载链接】zephyr 项目地址: https://gitcode.com/GitHub_Trending/ze/zephyr

本文以 Zephyr 仓库中 boards/acrn/ 板级支持为核心,系统讲解如何让 Zephyr 作为 x86 ACRN 管理型虚拟化器(hypervisor)下的“预启动(pre-launched)”实时 VM 运行:从选择 acrn 系列板级目标并构建 Zephyr 原始镜像,到手工编辑 ACRN 的 hybrid.xml 完成内存布局与 SMP CPU 亲和性配置,再到制作 GRUB + EFI 启动介质并在控制台上看到 Hello World! acrn 的完整实战流程。读完本文,你将掌握 Zephyr 与 ACRN hypervisor 的对接参数(kern_load_addrkern_entry_addrcpu_affinity)的确定方法,以及该板级目标在仓库中的 Kconfig、devicetree 与 defconfig 的实际构成。

ACRN Hybrid 用户场景:ACRN hypervisor 同时托管 Zephyr 预启动 VM 与 SOS VM

ACRN Hybrid 场景与“预启动”VM

ACRN(Project ACRN)是一款面向 x86 的管理型虚拟化器,Zephyr 可以在其下作为 guest 运行。与“先启动 Linux SOS VM、再由其拉起实时 VM”的两级模式不同,ACRN 的 Hybrid 场景支持 Zephyr 以所谓 "pre-launched"(预启动)模式 直接运行:Zephyr 在启动时直接访问 ACRN hypervisor,不经过 SOS VM。这也是原文档指出的最贴近真实用户场景的模式——因为 Zephyr 的实时性与功能安全特性不会受到其他 VM 的干扰,Zephyr 镜像在 ACRN 中会被标记为 vm_typeSAFETY_VM 的类型。

该板级支持的入口文档位于 boards/acrn/acrn/doc/index.rst,由板级索引页 boards/acrn/index.rst 通过 toctree(**/* 通配)聚合。

板级目标定义:acrn / acrn_ehl_crb / acrn_adl_crb

仓库中 ACRN 板级支持定义了三个目标,均可在 board.yml 中确认:

板级名称全称说明
acrnACRN hypervisor通用 ACRN 目标
acrn_ehl_crbACRN on EHL hypervisorIntel Elkhart Lake(EHL)参考板 CRB
acrn_adl_crbACRN Hypervisor on ADLIntel Alder Lake(ADL)参考板 CRB

三者的 Kconfig 组织方式一致:顶层 Kconfig 定义公共选项并强制选择 64 位 x86:

config ACRN_COMMON
	bool
	default y if BOARD_ACRN || BOARD_ACRN_EHL_CRB || BOARD_ACRN_ADL_CRB
	select CPU_HAS_FPU
	select X86_64

各具体板级(如 Kconfig.acrnKconfig.acrn_ehl_crbKconfig.acrn_adl_crb)则各自声明 config BOARD_*select SOC_ATOM,即把这三个目标都挂到 Intel Atom 系列 SoC 描述之下。board.cmake 中仅配置了 misc-flasher 作为默认刷写器,因为该板级的实际“部署”是烧录 EFI 启动介质,而非串口/调试器直连:

board_set_flasher_ifnset(misc-flasher)
board_finalize_runner_args(misc-flasher)

从源码结构看,板级还附带一个专门的 C 文件 board_acrn.c,内容只有 4 行——它声明了各逻辑 CPU 的本地 APIC ID 映射表:

/* Local APIC IDs for each logical CPU */
uint8_t x86_cpu_loapics[] = { 0x00, 0x02, 0x04, 0x06 };

这个 0x00/0x02/0x04/0x06 的偶数递增序列与下文 ACRN 侧把 SMP CPU 固定到物理核 0、1、2、3 的配置相互对应,是 Zephyr 侧 SMP 能跑通的前提之一。

步骤一:构建 Zephyr 应用

按照常规流程构建即可,选择 ACRN 板级目标(原文档以 EHL CRB 为例,它是该场景的建议平台之一):

west build -b acrn_ehl_crb samples/hello_world

构建完成后有两点必须注意,这也是整个对接中最容易踩坑的地方:

  1. 检查 build/zephyr/.config 的 kconfig 输出。后续配置 ACRN 时需要引用其中的 CONFIG_MP_MAX_NUM_CPUS 等值,这一点在配置 CPU 亲和性时会用到。
  2. 部署产物是 build/zephyr/zephyr.bin(raw memory image),而不是 zephyr.elf。与其他 x86 目标不同,ACRN 不认识 ELF,它只会把一段连续数据区加载进内存并跳转到指定地址,所以不能使用 ELF 镜像。

之所以强调“加载地址 + 入口地址”这两个值,是因为它们必须在 ACRN 配置中手工填写,而它们的来源分别是:

  • 加载地址:必须与 include/zephyr/arch/x86/memory.ld 中的 LOCORE_BASE 符号一致:

    #define LOCORE_BASE         0x1000
    #define LOCORE_SIZE         (0x10000 - LOCORE_BASE)
    ...
    LOCORE (wx)     : ORIGIN = LOCORE_BASE, LENGTH = LOCORE_SIZE
    

    当前该值为 0x1000,与 ACRN 默认配置恰好一致。

  • 入口地址:从构建出的 zephyr.elf 中用 binutils 提取:

    $ objdump -f build/zephyr/zephyr.elf
    
    build/zephyr/zephyr.elf:     file format elf64-x86-64
    architecture: i386:x86-64, flags 0x00000012:
    EXEC_P, HAS_SYMS
    start address 0x0000000000001000
    

    默认情况下入口地址也是 0x1000,但原文档特别提醒:这并非在所有配置下恒真,未来也可能变化,因此每次换配置后都应重新确认,而不是照抄 0x1000

板级 defconfig 与 devicetree 说明了什么

从仓库中的默认配置可以读出该板级的运行时画像。通用目标 acrn_defconfig

CONFIG_PIC_DISABLE=y
CONFIG_LOAPIC=y
CONFIG_APIC_TSC_DEADLINE_TIMER=y
CONFIG_X2APIC=y
CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
CONFIG_BUILD_OUTPUT_BIN=y
CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
CONFIG_KERNEL_VM_SIZE=0x1000000

而 EHL CRB 目标 acrn_ehl_crb_defconfig 在此基础上多了几项关键差异:

CONFIG_SMP=y
CONFIG_X2APIC=y
CONFIG_PCIE=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=1900000000
CONFIG_APIC_TIMER_IRQ_PRIORITY=1
  • CONFIG_SMP=y:印证了 EHL CRB 场景默认按多核构建,这正是必须在 ACRN 侧配置 cpu_affinity 的原因;
  • 系统时钟从通用的 25 MHz 提升到 1.9 GHz,对应真实平台 CPU 频率;
  • CONFIG_BUILD_OUTPUT_BIN=y:与“必须交付 zephyr.bin raw 镜像”的要求互相呼应。

对应的 acrn.dts 则描述了 guest 视角的硬件模型:

/ {
	model = "ACRN";
	compatible = "acrn";

	cpus {
		cpu@1 {
			device_type = "cpu";
			compatible = "intel,x86_64";
			d-cache-line-size = <64>;
			reg = <1>;
		};
	};

	chosen {
		zephyr,sram = &dram0;
		zephyr,console = &uart0;
		zephyr,shell-uart = &uart0;
	};
};

&uart0 {
	status = "okay";
	current-speed = <115200>;
};

其中 DT_DRAM_SIZE 定义为 8 MB(DT_SIZE_K(8192))、DT_DRAM_BASE 为 0,并通过 #include <intel/atom.dtsi> 复用 Intel Atom 的 SoC 设备树片段。从结构上看,Zephyr 侧只显式声明了 cpu@1(加上继承自 atom.dtsi 的 cpu@0),即 guest 内可见两核,与后文把 Zephyr 固定到物理 CPU 0/1 的推荐做法一致;console 与 shell 均走 uart0(115200 波特率)。

步骤二:获取并配置 ACRN

首先获取 ACRN hypervisor 源码:

git clone https://github.com/projectacrn/acrn-hypervisor

版本上建议使用 v2.5.1 或更新版本,因为新版本对 Zephyr 侧 SMP 的支持更好。

ACRN 和 Zephyr 都偏好构建期配置而非运行时探测,但两者的配置形态差异很大:Zephyr 使用可合并的 kconfig defconfig 与 devicetree include 等小单元,而 ACRN 是单个大型 XML 文件。你需要选择与硬件匹配的主机配置(本例为 ehl-crb-b),然后编辑 misc/config_tools/data/<platform>/hybrid.xml,按以下三部分修改:

1. 让 Zephyr 成为 <vm id="0">

<vm> 声明列表中,确认 Zephyr 镜像的 id 属性为 0。这样启动 ACRN 时只需要携带一个 VM 镜像,避免为了启动而把庞大的 Linux 镜像拷贝进启动文件系统。在已测试过的配置下,Zephyr 的 <vm_type> 固定为 SAFETY_VM

2. 配置 Zephyr 内存布局

找到 <vm id="0">...<os_config> 段,使其形如:

<os_config>
    <name>Zephyr</name>
    <kern_type>KERNEL_ZEPHYR</kern_type>
    <kern_mod>Zephyr_RawImage</kern_mod>
    <ramdisk_mod/>
    <bootargs></bootargs>
    <kern_load_addr>0x1000</kern_load_addr>
    <kern_entry_addr>0x1000</kern_entry_addr>
</os_config>

两个地址参数的前文对应关系再强调一次:

  • <kern_load_addr> 必须匹配 memory.ld 中的 LOCORE_BASE(当前为 0x1000);
  • <kern_entry_addr> 必须匹配 objdump -f zephyr.elf 输出的 start address

注意 <kern_mod>Zephyr_RawImage</kern_mod> 这一标签——它后面会与 GRUB 的 module2 行配合,把 raw 镜像按这个名字传给 ACRN(见步骤四)。

3. 配置 CPU 亲和性(SMP 关键点)

Zephyr 默认按 SMP 构建,而 ACRN 默认配置只给 guest 一个 CPU。做法是:在 Zephyr 的 .config 中查 CONFIG_MP_MAX_NUM_CPUS,然后在 <vm id="0"><cpu_affinity> 里给出同样数量的 <pcpu_id>

<vm id="0">
    <vm_type>SAFETY_VM</vm_type>
    <name>ACRN PRE-LAUNCHED VM0</name>
    <guest_flags>
        <guest_flag>0</guest_flag>
    </guest_flags>
    <cpu_affinity>
        <pcpu_id>0</pcpu_id>
        <pcpu_id>1</pcpu_id>
    </cpu_affinity>
    ...
    <clos>
        <vcpu_clos>0</vcpu_clos>
        <vcpu_clos>0</vcpu_clos>
    </clos>
    ...
</vm>

这一步是硬性要求:不做此修改,ACRN hypervisor 与 Zephyr 应用都无法成功启动(仅限 SMP 构建);若确定只用单核运行 Zephyr,则可以跳过。

同时必须调整其他 VM 的占用,避免物理核冲突。例如把 SOS VM 挪到 CPU2/CPU3:

<vm id="1">
    <vm_type>SOS_VM</vm_type>
    <name>ACRN SOS VM</name>
    <guest_flags>
        <guest_flag>0</guest_flag>
    </guest_flags>
    <cpu_affinity>
        <pcpu_id>2</pcpu_id>
        <pcpu_id>3</pcpu_id>
    </cpu_affinity>
    <clos>
        <vcpu_clos>0</vcpu_clos>
        <vcpu_clos>0</vcpu_clos>
    </clos>
    ...
</vm>

<pcpu_id> 是宿主机上的物理 CPU 索引。多 guest 共存时不应让这些分配相互重叠;本例只用 0/1 两个核做测试即可。原文档还指出 ehl-crb-b 有 4 个物理 CPU,把 0–3 全部分给 Zephyr 也能工作,但那样就没有空闲核留给其他 guest 独占了。Zephyr 侧 board_acrn.cx86_cpu_loapics[] = {0x00, 0x02, 0x04, 0x06} 恰好为最多 4 个逻辑核预留了本地 APIC ID,与上述“4 核全给 Zephyr”的上限相符。

4. 构建 ACRN

配置完成后构建:

$ make -j BOARD=ehl-crb-b SCENARIO=hybrid

所需产物只有一个:build/hypervisor/acrn.bin(ACRN multiboot 镜像)。

步骤三:组装 EFI 启动介质

ACRN 通过 GNU GRUB 引导,GRUB 又由 EFI 固件启动,三层都需要配置正确。

获取 GRUB

需要与硬件匹配的 GRUB EFI 二进制。多数情况下从源码构建或从某个 Linux 发行版拷贝即可;但在某些硬件上需要为特定平台打补丁才能工作——原文档对此的忠告颇具实践味道:去问问上一个成功测试的人,直接拷贝一份能用的二进制往往最快。

创建 EFI 启动分区

把启动介质(例如挂在 Linux 上的 /dev/sdb,具体设备视硬件而定)接上,创建一个足够大的 EFI 引导分区(类型码 0xEF)。原文档给出了一条把命令序列直接喂给 fdisk 的写法,也可以手工敲入同样的按键序列:

# for i in n p 1 "" "" t ef w; do echo $i; done | fdisk /dev/sdb
...
<大量 fdisk 输出>

随后在新分区上创建 FAT 文件系统并挂载:

# mkfs.vfat -n ACRN_ZEPHYR /dev/sdb1
# mkdir -p /mnt/acrn
# mount /dev/sdb1 /mnt/acrn

拷贝镜像并配置 GRUB

ACRN 自身没有运行时文件系统,它的 guest VM(即 zephyr.bin)是作为 GRUB 的 "multiboot" module 传入的,因此必须依赖 GRUB 的文件系统驱动。三个文件(GRUB、ACRN、Zephyr)都要放进启动介质的 /efi/boot 目录,且 GRUB 必须命名为 bootx64.efi 才能被固件识别为引导程序:

# mkdir -p /mnt/acrn/efi/boot
# cp $PATH_TO_GRUB_BINARY /mnt/acrn/efi/boot/bootx64.efi
# cp $ZEPHYR_BASE/build/zephyr/zephyr.bin /mnt/acrn/efi/boot/
# cp $PATH_TO_ACRN/build/hypervisor/acrn.bin /mnt/acrn/efi/boot/

启动时 GRUB 会读取 efi/boot/grub.cfg 作为运行时配置。它需要把 acrn.bin 作为 boot target 加载,并把 zephyr.bin 作为第一个 module 传入(因为 Zephyr 被配置为 <vm id="0">)。原文档给出的这份最小配置在绝大多数硬件上都能工作(hd0 几乎总指 GRUB 所在的启动文件系统),无需折腾 GRUB 插件、菜单或超时:

# cat > /mnt/acrn/efi/boot/grub.cfg<<EOF
set root='hd0,msdos1'
multiboot2 /efi/boot/acrn.bin
module2 /efi/boot/zephyr.bin Zephyr_RawImage
boot
EOF

其中 module2 /efi/boot/zephyr.bin Zephyr_RawImage 行尾的 Zephyr_RawImage 与 XML 里 <kern_mod>Zephyr_RawImage</kern_mod> 的取值一一对应,是 GRUB 与 ACRN 之间的“镜像名契约”。完成后卸载并同步:

# umount /dev/sdb1
# sync

步骤四:启动并验证

一切正常时,用该 EFI 介质启动硬件后,你会得到:一个运行中的 ACRN、一个运行中的 Zephyr(默认即为 prelaunched VM),以及一个可用的 ACRN 控制台命令行。

在 ACRN 控制台用 vm_console 命令查看 Zephyr(VM 0)的输出:

ACRN:>vm_console 0

----- Entering VM 0 Shell -----
*** Booting Zephyr OS build v2.6.0-rc1-324-g1a03783861ad  ***
Hello World! acrn

看到 Hello World! acrn 即代表 Zephyr 已在 ACRN 预启动 VM 中成功运行。

适用前提与限制小结

结合原文档与仓库文件,把关键前提浓缩如下,便于核对:

  • 适用平台:Intel EHL CRB(acrn_ehl_crb,ACRN 主机配置 ehl-crb-b)与 ADL CRB(acrn_adl_crb),以及通用 acrn 目标;三者均 select SOC_ATOM,运行于 X86_64
  • ACRN 版本建议 v2.5.1+(SMP 支持);
  • Zephyr 交付物必须是 zephyr.bin raw 镜像,kern_load_addrLOCORE_BASE0x1000,见 include/zephyr/arch/x86/memory.ld),kern_entry_addrobjdump -f 输出的 start address;
  • SMP 构建时必须手工配置 <cpu_affinity> 并让其他 VM 让出对应物理核,否则 ACRN 与 Zephyr 均无法启动;
  • guest 内可见硬件由 acrn.dts 描述:8 MB DRAM、uart0/uart1(115200)、intel,x86_64 CPU 及 PCIE 控制器节点,运行时时钟源由 defconfig 的 SYS_CLOCK_HW_CYCLES_PER_SEC 决定(通用目标 25 MHz,EHL CRB 1.9 GHz)。

关于 ACRN 自身更多能力(构建与使用 SOS VM、其他 guest VM 等)的完整资料,属于 ACRN 上游文档范畴,本文仅覆盖 Zephyr 侧与该仓库板级支持直接相关的内容。

【免费下载链接】zephyr Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures. 【免费下载链接】zephyr 项目地址: https://gitcode.com/GitHub_Trending/ze/zephyr

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值