2. QEMU ARM Versatile Express启动

任务说明

本案例目标是在主机上使用QEMU模拟ARM Versatile Express(vexpress-a9)开发板,启动一个Linux内核,并挂载由SD卡镜像提供的根文件系统,最终进入可交互的shell。完成本案例后,你将拥有一个不依赖真实硬件、可重复启动的ARM Linux最小运行环境。

环境准备与依赖

需要一台Linux主机(x86_64或aarch64均可),并安装以下软件包:

  • qemu-system-arm(版本≥2.8,推荐≥6.0)
  • wget、tar、xz(用于下载与解压)
  • e2fsprogs(提供mkfs.ext4与debugfs,用于制作根文件系统镜像)
  • busybox(静态编译的二进制,用于构建根文件系统内容)

以下操作均在普通用户权限下完成,不需要root。所有文件放置于同一工作目录,例如~/vexpress-lab。

获取内核与设备树

直接使用Linux内核社区发布的预编译ARM测试内核,避免从源码编译的耗时。下载vexpress平台对应的内核镜像与设备树文件:

mkdir -p ~/vexpress-lab && cd ~/vexpress-lab
wget https://github.com/raspberrypi/linux/archive/refs/tags/raspberrypi-kernel_1.20230405-1.tar.gz
# 注:该仓库包含预编译的vexpress内核,但体积较大。更轻量做法是直接使用发行版内核包:
# Debian/Ubuntu: apt-get install linux-image-armmp 后提取 /boot/vmlinuz-*-armmp
# 此处为保持最小依赖,改用kernel.org的buildroot预编译内核:
wget https://buildroot.org/downloads/buildroot-2023.02.tar.gz
tar xf buildroot-2023.02.tar.gz
# 使用buildroot内置的vexpress_defconfig编译内核(约5分钟)
cd buildroot-2023.02
make qemu_arm_vexpress_defconfig
make linux-rebuild
# 生成的内核位于 output/images/zImage,设备树位于 output/images/vexpress-v2p-ca9.dtb
ls -l output/images/zImage output/images/vexpress-v2p-ca9.dtb

若上述buildroot方式因网络原因不可用,可直接下载预编译产物:

cd ~/vexpress-lab
wget https://github.com/BPI-SINOVOIP/BPI-M64-bsp/raw/master/linux/arch/arm/boot/zImage  # 仅示例,实际请用下方命令
# 推荐稳定来源:使用kernel.org的CI产物
wget https://storage.googleapis.com/linux-kernel-ci/kernel/arm/multi_v7/linux-6.1.28-arm-zImage
wget https://storage.googleapis.com/linux-kernel-ci/kernel/arm/multi_v7/linux-6.1.28-arm-dtb/vexpress-v2p-ca9.dtb
mv linux-6.1.28-arm-zImage zImage
mv linux-6.1.28-arm-dtb/vexpress-v2p-ca9.dtb .

验证文件类型:

file zImage vexpress-v2p-ca9.dtb
# 预期输出:zImage: ARM OpenFirmware Device Tree Binary Data / vexpress-v2p-ca9.dtb: Device Tree Blob

制作根文件系统镜像

使用BusyBox构建最小根文件系统,并打包为ext4格式的SD卡镜像。以下脚本一次性完成全部步骤:

cd ~/vexpress-lab
# 1. 下载静态busybox
wget https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox  # 注意:这是x86静态版,ARM版需用下面交叉编译
# 正确做法:下载busybox源码并静态交叉编译
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j$(nproc)
# 若没有arm交叉编译器,先安装:sudo apt-get install gcc-arm-linux-gnueabi
cd ~/vexpress-lab
mkdir -p rootfs/{bin,sbin,etc,proc,sys,dev,lib,usr/{bin,sbin},root}
cp busybox-1.36.1/busybox rootfs/bin/
# 2. 创建设备节点与配置文件
sudo mknod -m 622 rootfs/dev/console c 5 1
sudo mknod -m 666 rootfs/dev/null c 1 3
cat <<EOF > rootfs/etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::restart:/sbin/init
EOF
mkdir -p rootfs/etc/init.d
cat <<EOF > rootfs/etc/init.d/rcS
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "VExpress RootFS Ready"
EOF
chmod +x rootfs/etc/init.d/rcS
# 3. 生成ext4镜像(大小64MB)
dd if=/dev/zero of=rootfs.ext4 bs=1M count=64
mkfs.ext4 -F rootfs.ext4
mkdir -p /tmp/mntpoint
sudo mount -o loop rootfs.ext4 /tmp/mntpoint
sudo cp -a rootfs/* /tmp/mntpoint/
sudo umount /tmp/mntpoint
echo "rootfs.ext4 created"

上述脚本中arm-linux-gnueabi-交叉编译器需提前安装。若主机为arm64架构,可直接使用gcc编译busybox并替换CROSS_COMPILE为空。

启动QEMU并验证

执行以下命令启动模拟器:

cd ~/vexpress-lab
qemu-system-arm \
  -M vexpress-a9 \
  -m 512M \
  -kernel zImage \
  -dtb vexpress-v2p-ca9.dtb \
  -drive file=rootfs.ext4,if=sd,format=raw \
  -append "console=ttyAMA0 root=/dev/mmcblk0 rw" \
  -nographic

参数说明:

  • -M vexpress-a9:指定机器类型为Versatile Express Cortex-A9开发板
  • -kernel:加载Linux内核镜像
  • -dtb:加载设备树二进制文件
  • -drive:将rootfs.ext4作为SD卡设备挂载
  • -append:传递内核命令行,root=/dev/mmcblk0指定根文件系统位于SD卡
  • -nographic:不使用图形窗口,串口重定向到当前终端

预期输出与判定标准

启动后终端应依次输出内核启动日志,包含以下关键信息:

Booting Linux on physical CPU 0x0
Linux version 6.1.28 (builder@host) ...
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
Machine model: V2P-CA9
...
VFS: Mounted root (ext4 filesystem) on device 179:0.
Freeing unused kernel memory: 1024K
Run /sbin/init as init process
VExpress RootFS Ready
/ #

判定标准:

  1. 出现Machine model: V2P-CA9,确认设备树加载正确;
  2. 出现Mounted root (ext4 filesystem),确认根文件系统挂载成功;
  3. 最终出现/ #提示符,可输入ls、uname -a等命令并得到响应,说明系统完全启动。

在/ #提示符下执行uname -a,预期输出包含armv7l和GNU/Linux字样。输入poweroff -f可关闭模拟器。

故障排查

  • 若提示kernel too old,说明宿主机内核无法识别zImage格式,改用-kernel加载zImage时需确保文件为ARM格式(file zImage应显示ARM)。
  • 若挂载根文件系统失败,检查rootfs.ext4是否包含/sbin/init或/bin/sh,并确认root=/dev/mmcblk0与if=sd对应。
  • 若串口无输出,尝试去掉-nographic改用-serial stdio,并确认console=ttyAMA0与vexpress-a9的串口一致。
宿主机 (Linux) zImage (ARM内核) vexpress-v2p-ca9.dtb rootfs.ext4 (ext4镜像) qemu-system-arm -M vexpress-a9 QEMU 模拟的 Versatile Express (vexpress-a9) Cortex-A9 ARMv7 单核 L2内存 512MB GIC 中断控制器 UART0 ttyAMA0 SD/MMC控制器 PL181 Linux内核 从zImage解压到内存 根文件系统 (ext4) 挂载于 /dev/mmcblk0 VFS: Mounted root (ext4 filesystem) / # _ 加载 加载 SD镜像映射 控制台输出 模拟硬件 外设 软件组件