跳到主要内容

Axvisor 测试

Axvisor 复用了与 StarryOS 测试 相同的用例发现、资产准备和结果判定基础设施,但不支持 test_commands 分组命令。需要执行命令的 Axvisor 用例使用 shell_check_steps,明确指定提示符、命令和完成条件。

测试编排(用例发现、分组构建、资产准备、结果判定)由 scripts/axbuild/src/test/ 提供统一框架,核心原则是 OS 只构建一次,逐 case 运行。共享框架的完整说明见 测试基础设施;本文描述 Axvisor 特有的测试目录结构、三种测试模式(QEMU / U-Boot / Board)的差异,以及 Axvisor 独有的 test uboot 模式。

1. 测试接口​

Axvisor 将 QEMU、U-Boot 和板卡测试置于同一入口下,但每种模式选择不同的运行资产和结果判定路径。命令参数如下,test uboot 是三套系统中唯一的 U-Boot 测试接口。

cargo xtask axvisor test qemu [--test-group <g>] [--test-case <c>[,<c>...]]... [--list]
cargo xtask axvisor test uboot --board <type> [--guest <image>] [--uboot-config <cfg>] # Axvisor 独有
cargo xtask axvisor test board --board <type> --server <h> --port <p> [--test-case <c>] [--list]

2. 用例发现​

Axvisor 测试资产位于:

test-suit/axvisor/
└── normal/
└── <case>/
└── qemu-{arch}.toml

与 StarryOS 的平铺结构不同,Axvisor 用 normal 测试组目录组织用例。发现算法统一通过 build-{target}.toml 定位构建组、qemu-{arch}.toml 定位用例。

普通功能用例位于 test-suit/axvisor/,应用及部分 Nightly 场景位于 apps/axvisor/。axvisor/test/discovery.rs::CaseSource 将两者分开:test qemu 和 test board 使用 Tests,app qemu 和 app board 使用 Apps,不会跨命名空间扫描。板卡测试的 normal 组另保留 benchmarks/axvisor/ 的兼容入口。应用入口与选择规则见第 8 节。

3. 运行模式​

三种模式共享 case 发现和构建组概念,但宿主环境、启动链路及筛选参数不同。下表用于在 CI 或板端故障时选择正确的复现入口。

模式命令运行环境适用场景
test qemucargo xtask axvisor test qemuQEMU 虚拟机常规功能验证(CI 主力)
test ubootcargo xtask axvisor test uboot(Axvisor 独有)远程板卡 + U-Boot 引导验证 hypervisor 在真实硬件 + U-Boot 链路上的行为
test boardcargo xtask axvisor test board远程板卡板级回归

3.1 QEMU 测试​

最常用的测试模式,在 QEMU 中启动 Axvisor 和配置的 Guest VM。执行链位于 axvisor/test/qemu.rs::test_qemu(),采用两阶段策略:先编译所有 build group,再运行所有 QEMU 用例。vm_configs 只构建 /guest/builtin catalog;每个需要开机客户机的 QEMU case 都必须在 host cmdline/-append 中显式写入 axvisor.vm_start=<id>[,<id>...],没有 selector 的 case 应验证零启动或通过 shell/HTTP 按需启动。

两阶段设计(Phase 1 / Phase 2)的动机:先暴露所有编译错误,避免在 QEMU 上浪费时间后才发现某个 build group 无法编译。

关键步骤的源码行为:

步骤源码位置行为
用例发现discovery.rs::discover_qemu_cases()按 CaseSource 扫描 test-suit/axvisor/<group>/ 或 apps/axvisor/<group>/,默认 group 为 normal
VM 配置qemu_group_build_context()从用例 host-initramfs.toml 读取 VM 打包输入,缺省时使用构建配置的列表;只产生 builtin catalog
rootfs 准备rootfs::ensure_qemu_rootfs_ready()根据每个用例的宿主根盘接线准备磁盘;纯 initramfs 用例不下载宿主根盘
分组命令校验discovery.rs::load_qemu_case()在构建前拒绝非空 test_commands,提示改用 shell_check_steps
结果判定QemuTestSummary收集所有 case 的 pass/fail,最终 finish_with_total_detail() 统一判定退出码

单个 case 运行(run_qemu_case → load_qemu_case_config):保留显式 shell_check_steps、应用 apply_timeout_scale、准备 rootfs 资产(走共享 test/case/ 层)、以 RootfsWritePolicy::Discard patch rootfs 路径。Axvisor 直接保留目标串口输出,由目标侧 AXBT map 完成回溯符号化。宿主 selector 属于 QEMU/板卡 cmdline,不要写入 guest VM TOML 的 [kernel].cmdline。

3.2 U-Boot 测试​

Axvisor 是唯一支持 U-Boot 测试模式的子系统。cargo xtask axvisor test uboot --board <TYPE> 在远程板卡上通过 U-Boot 引导 Axvisor 和 Guest。执行链位于 axvisor/test/board.rs::test_uboot()。

参数说明
--board <TYPE>(必需)ostool-server 上的板卡类型
--guest <IMAGE>指定 guest 镜像(默认 linux)
--uboot-config <CFG>U-Boot 配置文件,省略时自动发现

关键步骤:

  • 用例定位:discover_uboot_test_group() 按 board 名和 guest 名定位唯一的 board test group。
  • U-Boot config 合并:merge_board_test_uboot_config() 把 base config(来自 --uboot-config 或自动发现)与 board test config(来自 board-test-*.toml)合并。合并策略:board test 的 fail_regex、uboot_cmd 以及有序 shell_check_steps 整体覆盖 base;步骤内使用可选的 shell_prefix、shell_cmd 和成功/失败判定,无命令步骤可只检查自行产生的输出。地址类字段(kernel_load_addr、fit_load_addr、bootm_addr)仅在 board test 提供时覆盖;base 的 local(串口、波特率)和 dtb_file 保留。cmdline 同样整体覆盖:board test 的 cmdline(或 uboot_cmd 中的 setenv bootargs)是宿主 bootargs 的唯一来源,用例必须写入该板卡所需的全部宿主启动参数和 axvisor.vm_start(接磁盘根的板卡含 root=、console=、earlycon 等;纯 initramfs、无磁盘根切换的板卡(如 ASUS NUC15CRH 的 x86_64 httpboot 用例)可以只含 selector),不存在向板卡环境 ${bootargs} 或 base preset 的隐式追加;远端 test board 路径不经合并,行为与此一致。
  • 编译与运行:app.uboot() 一次性完成编译和 U-Boot 运行,由合并后的 U-Boot config 判定结果。

该模式验证完整的"U-Boot → Axvisor → Guest"引导链路,覆盖真实硬件上 U-Boot 加载 Axvisor ELF、Axvisor 初始化硬件虚拟化扩展、再启动 Guest 的全流程。

3.3 板卡测试​

板级测试通过 board-{board_name}.toml 配置文件定义。执行链位于 axvisor/test/board.rs::test_board(),使用 BoardTestRunState 逐 group 运行并收集结果(与 QEMU 测试的 QemuTestSummary 类似)。

每个 board test group 的处理:

  1. prepare_request() 加载 build config(SnapshotPersistence::Discard)
  2. load_cargo_config() 装配 Cargo
  3. load_board_config() 加载 board test config(board-test-*.toml)
  4. app.board() 编译 + 部署到远程板卡,由 board config 的正则判定结果

--test-case 和 --board 支持按用例名和板卡名过滤;--list 列出所有 board test group。发现算法通过 discover_board_test_groups() 递归扫描,board 配置按板卡名命名(board-{name}.toml),通过 nearest_build_wrapper() 向上查找最近的构建配置。

ROCK 4D 用例将构建机上的 BSP kernel 和 guest DTB 打入宿主归档,运行前必须准备这两项本地资产。完整命令见 ROCK 4D Linux Guest。

4. 资产管线​

Axvisor 复用共享资产管线中的 C、Shell、Python、Rust 和 Plain 类型。test_commands 在进入资产管线前已被拒绝;其他类型由 resolve_case_pipeline() 检测,同一目录同时出现多个 pipeline 触发条件会直接报错:

Pipeline触发条件Axvisor 使用情况
Groupedtest_commands 非空不支持,在用例发现阶段报错
C含 c/ 子目录C 测试程序
Shell含 sh/ 子目录shell 脚本测试
Python含 python/ 子目录Python 测试
Rust含 rust/ 子目录(须含 Cargo.toml)Rust 测试程序(交叉编译为 musl 静态二进制)
Plain以上均不满足最常见,纯 QEMU 启动验证

pipeline 类型、检测优先级、资产准备、rootfs 缓存和 grouped runner 协议的完整说明见 测试基础设施。Axvisor 的 prepare_staging_root 钩子为空操作(|_| Ok(())),不做 StarryOS 那样的 DNS 注入和 APK 区域配置。

5. x86 嵌套 OVMF/ACPI 验证​

normal 组提供两条对称的 x86 嵌套 OVMF 用例:

cargo xtask axvisor test qemu --arch x86_64 --test-group normal \
--test-case ovmf-acpi-vmx
cargo xtask axvisor test qemu --arch x86_64 --test-group normal \
--test-case ovmf-acpi-svm

两者共用 test-suit/axvisor/normal/qemu-acpi-ovmf/x86-linux-acpi-ovmf.toml、同一 BusyBox initramfs 和同一 4 MiB guest firmware 输出。build config 不选择 vmx 或 svm Cargo feature;唯一的 backend 差异是外层 QEMU 的 -cpu 能力:VMX 暴露 EPT、unrestricted guest 和 flexpriority,SVM 暴露 SVM、NPT 和 NRIP save。因此应分别在 Intel/VMX 与 AMD/SVM KVM 宿主上运行对应 case。

两个 case 均已加入 CI 运行:ovmf-acpi-vmx 在 self-hosted Intel/KVM 宿主上运行(见 .github/workflows/ci.yml 中 "Test axvisor x86_64 ACPI direct and OVMF boot (vmx)" 任务),ovmf-acpi-svm 在 self-hosted AMD/KVM 宿主上运行(见单个 kvm-amd SVM 任务)。

资产准备复用 Ostool 的 x86_64 OVMF 缓存,可先用以下命令确认来源路径:

cargo xtask ovmf --arch x86_64

Axvisor 测试构建日志会输出本次实际使用的 Ostool CODE、VARS 和最终 guest image 的路径、字节数及 SHA-256,同时说明布局是 split CODE/VARS 还是 monolithic CODE。对于 monolithic CODE,VARS 仍会记录来源证据,但明确标记为 unused;这些运行时摘要不固化到仓库。

成功条件 AXVISOR_X86_OVMF_ACPI_PASSED 来自嵌套 Linux initramfs,而不是 QEMU 外层 OVMF。该 marker 只有在 OVMF 完成 kernel handoff、Linux 进入早期用户态,并且 Linux 能读取 DSDT、APIC、FACP、SPCR、MCFG,在 /proc/iomem 中登记 Q35 PCI MMCONFIG 区间,发现 ttyS0、初始化 IOAPIC 且 online CPU 集合为 0 时才会输出。因此它同时证明当前 firmware handoff 和 guest-side ACPI 接受路径。

同一 MMCONFIG 检查也由 x86 pci-enumeration initramfs 执行;该用例继续通过 ECAM 验证客户机 PCI endpoint 枚举。Q35 固定 ECAM 的资源所有权、PCIEXBAR 只读语义及 direct/OVMF firmware 描述见 x86 Q35 ECAM 设计。

当前用例仍由 fw_cfg 提供 Linux kernel、initramfs 和命令行。它不证明 OVMF 已枚举 Axvisor guest PCI 启动盘,也不证明 Linux 经 guest ESP 或 EFI stub 启动。

6. VMX 合并任务​

CI 使用一个 kvm-intel runner 运行八个 VMX 用例。原有 smoke、ACPI、PCI 枚举和 PCI block 七个用例保持同一 Cargo 编译身份,统一启用 ax-driver/nvme 与 web-ui;vm_configs 只作为每个用例的运行时输入。新增的 web-ui-vmx 使用独立的默认网页构建配置并额外启用 ax-driver/intel-net,因此运行器按两种 Cargo 身份分别构建,再执行各自的用例。prepare_case_build_groups() 会按 Cargo 身份合并前七个用例,确保 Web UI 与 NVMe 栈在无向量 x86_64 目标上的编译守卫只构建一次:

cargo xtask axvisor test qemu --arch x86_64 \
--test-case smoke-vmx,direct-acpi-vmx,mp-fallback-vmx,ovmf-acpi-vmx,pci-enumeration-vmx,pci-block-rw-vmx,pci-block-ro-vmx,web-ui-vmx

web-ui-vmx 从空客户机登记表开始,通过 e1000 转发访问网页与 defaults 接口,复用原始镜像里的 Linux 内核和 Alpine 根盘。default_guest_probe.py 在 90 秒门限内验证真实 shell 算术及系统信息,再验证重连、停止删除与自动 ID 重用;成功标记为 AXVISOR_DEFAULT_GUEST_WEB_UI_PASSED。真实浏览器交互的操作步骤见默认客户机运行流程。

7. SVM 合并任务​

六个 SVM 用例共用 normal/qemu-svm/build-x86_64-unknown-none.toml,启用 ax-driver/nvme。host-initramfs.toml 分别声明 VM 配置、BusyBox 和 OVMF 输入。test_qemu() 按 Cargo 身份合并 build group 并复用内核,每个用例再恢复自己的 build TOML 路径读取运行时输入;各次启动打印相同内核哈希和各自的归档哈希。

6.1 连续运行​

--test-case 支持重复参数及逗号列表;单例失败后继续其余用例,QemuTestSummary 汇总并返回非零。一次运行全部 SVM 的命令为:

cargo xtask axvisor test qemu --arch x86_64 \
--test-case smoke-svm,direct-acpi-svm,ovmf-acpi-svm,pci-enumeration-svm,pci-block-rw-svm,pci-block-ro-svm

6.2 持续集成​

.github/ci/checks/axvisor.toml 只生成一个 kvm-amd SVM 作业,cache_key = "",上限 120 分钟。Smoke 使用 root=/dev/nvme0n1 rw,同时检查宿主磁盘读写和双 VM 磁盘隔离;其他用例从内存根加载对应归档。不同构建组保留各自产物。

8. 应用入口​

cargo xtask axvisor app 运行 apps/axvisor/ 下的应用,包含板卡场景与 QEMU 场景。axvisor/app.rs::app_command() 用 CaseSource::Apps 调用共享的 run_board_cases() 和 run_qemu_cases(),复用构建、部署与结果判定流程;test 入口不再包含这些应用。升级后,原有 test board --board orangepi-5-plus-linux 仍可能成功运行测试套件的 smoke,但不会执行应用目录中的 PCIe 网卡直通。

8.1 列表与目录选择​

app list 列出可用的板卡和 QEMU 应用。运行时通过 -g/--group 选择组,默认 normal;-t/--app 是运行配置所在目录相对 apps/axvisor/<group>/ 的路径,不含 normal/ 前缀,也不是配置文件名。

cargo xtask axvisor app list
cargo xtask axvisor app board -g normal \
-t board-ballpick-robot/pci-network/ping --list
cargo xtask axvisor app board -g normal \
-t board-ballpick-robot/kernel-types --list

discovery.rs::app_matches() 匹配精确目录及其后代,所以上例的 kernel-types 会选中其下多个子场景。可用逗号指定多个应用;未匹配的选择器报错。--list 只列出配置,不准备资源、不构建或占用板卡。实际运行必须提供 -t,避免无选择地运行所有应用。

8.2 板卡与 QEMU 运行​

板卡应用的 --board 过滤 board-<name>.toml 的逻辑名称,-b/--board-type 覆盖向板卡服务申请的物理类型,两者不能互换。未提供 -b 时使用配置中的 board_type;物理类型须与目标服务的 ostool board ls 输出一致。以下命令使用配置的默认服务连接设置,其他环境应补充 --server 和 --port。

cargo xtask axvisor app board -g normal \
-t board-ballpick-robot/pci-network/ping \
--board ballpick-robot-linux

执行前按PCIe 网卡应用说明准备板端镜像和网络。QEMU 应用使用相同的目录选择方式,并通过 --arch 或 --target 指定构建目标,例如:

cargo xtask axvisor app qemu --arch aarch64 \
-t qemu-timer-stress/gicv3-timer-stress

两类运行器都在宿主构建前调用 axvisor/test/guest_build.rs::prepare(),读取运行配置同目录下可选的 guest-builds.toml。其中 host_prepare 先准备共享资源,arceos_build_configs 再依次构建源码客户机;准备失败会阻止后续宿主构建与运行。没有该文件的应用继续使用自身配置和预先部署的镜像,运行前仍需满足各应用 README 的资源要求。