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
/ #
判定标准:
- 出现
Machine model: V2P-CA9,确认设备树加载正确; - 出现
Mounted root (ext4 filesystem),确认根文件系统挂载成功; - 最终出现
/ #提示符,可输入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的串口一致。