在嵌入式开发中,经常需要在x86主机上模拟ARM环境进行开发和调试。QEMU作为一款功能强大的开源模拟器,可以完美模拟各种ARM开发板。本文将详细介绍如何使用QEMU搭建完整的ARM开发环境,包括U-Boot引导、Linux内核加载和根文件系统挂载,并重点解决U-Boot环境变量持久化的问题。
首先需要准备以下组件:
安装ARM交叉编译工具链(以Ubuntu为例):
bash复制sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
编译QEMU(如果使用系统自带版本可跳过):
bash复制wget https://download.qemu.org/qemu-5.1.0.tar.xz
tar xvf qemu-5.1.0.tar.xz
cd qemu-5.1.0
./configure --target-list=arm-softmmu
make -j$(nproc)
sudo make install
提示:建议将编译好的qemu-system-arm添加到PATH环境变量中,方便后续使用。
bash复制wget ftp://ftp.denx.de/pub/u-boot/u-boot-2016.03.tar.bz2
tar xvf u-boot-2016.03.tar.bz2
cd u-boot-2016.03
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_ca9x4_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j$(nproc)
编译完成后会生成u-boot.bin文件,这是我们将要加载的U-Boot镜像。
bash复制wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.240.tar.xz
tar xvf linux-4.4.240.tar.xz
cd linux-4.4.240
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- zImage dtbs -j$(nproc)
编译完成后会生成两个关键文件:
可以使用BusyBox构建最小根文件系统,也可以下载现成的根文件系统镜像。本文使用一个512MB的ext3格式根文件系统镜像rootfs.ext3。
以下是完整的QEMU启动命令,我们逐项分析其作用:
bash复制../qemu5p1/qemu-5.1.0/arm-softmmu/qemu-system-arm \
-M vexpress-a9 -m 512M -nographic \
-smp 4 \
-device loader,file=./linux-4.4.240/arch/arm/boot/zImage,addr=0x68008000 \
-device loader,file=./linux-4.4.240/arch/arm/boot/dts/vexpress-v2p-ca9.dtb,addr=0x70000000 \
-device loader,file=./u-boot-2016.03/u-boot.bin,addr=0x60800000,cpu-num=0 \
-net nic,model=lan9118,macaddr=52:54:00:12:34:56 \
-net user,id=net0,hostfwd=tcp::2222-:22,hostname=qemu-arm \
-drive file=./rootfs.ext3,format=raw,if=sd,index=0 \
-drive file=./vexpress_flash.img,format=raw,if=pflash,unit=0,readonly=off \
-drive file=./vexpress_flash2.img,format=raw,if=pflash,unit=1,readonly=off
关键参数说明:
U-Boot环境变量默认存储在内存中,重启后会丢失。要实现持久化,需要将环境变量存储在非易失性存储器中。本文使用并行Flash(Parallel Flash)来实现这一目标。
首先创建两个Flash镜像文件:
bash复制dd if=/dev/zero of=vexpress_flash.img bs=1M count=64
dd if=/dev/zero of=vexpress_flash2.img bs=1M count=64
这两个64MB的镜像文件将作为并行Flash存储器使用。
在U-Boot中,需要配置环境变量存储位置。修改include/configs/vexpress_common.h:
c复制#define CONFIG_SYS_FLASH_BASE 0x40000000
#define CONFIG_ENV_IS_IN_FLASH 1
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x04000000)
#define CONFIG_ENV_SIZE 0x20000
#define CONFIG_ENV_SECT_SIZE 0x20000
这些配置指定了:
启动QEMU后,在U-Boot命令行中可以测试环境变量持久化:
bash复制# 设置环境变量
setenv myvar helloworld
saveenv
# 重启后检查
reset
printenv myvar
如果能看到myvar变量的值保持不变,说明环境变量已成功持久化到Flash中。
QEMU加载阶段:
U-Boot阶段:
内核加载阶段:
init=/init root=/dev/mmcblk0 rw console=ttyAMA0 rootwait rootfstype=ext3 mmci.pwr_delay=200 mmci.vccq=33内核启动阶段:
问题现象:内核panic或卡在启动过程中
可能原因及解决方案:
问题现象:saveenv后变量在重启后丢失
解决方案:
问题现象:无法ping通主机或外部网络
解决方案:
bash复制setenv serverip 10.0.2.2 # QEMU默认网关IP
ping 10.0.2.2
虽然我们指定了-smp 4参数模拟4核CPU,但在内核日志中可以看到只有1个CPU成功启动:
code复制CPU1: failed to boot: -38
CPU2: failed to boot: -38
CPU3: failed to boot: -38
Brought up 1 CPUs
这是因为默认的内核配置没有完全支持SMP。要启用完整的SMP支持,需要重新配置内核:
bash复制make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
确保以下选项启用:
code复制CONFIG_SMP=y
CONFIG_HOTPLUG_CPU=y
CONFIG_LOCAL_TIMERS=y
CONFIG_HAVE_ARM_SCU=y
CONFIG_HAVE_ARM_TWD=y
重新编译内核后,应该可以看到所有4个CPU核心都成功启动。
在内核启动的早期阶段,串口控制台可能还未初始化。要查看这些早期消息,可以:
earlyprintk参数:code复制earlyprintk=serial,ttyAMA0,115200
bash复制setenv bootargs 'init=/init root=/dev/mmcblk0 rw console=ttyAMA0 earlyprintk=serial,ttyAMA0,115200'
saveenv
要使用KGDB调试内核,需要:
配置内核支持KGDB:
bash复制make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
启用:
code复制CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
在QEMU启动参数中添加串口调试选项:
code复制-serial tcp::1234,server,nowait
在主机上使用gdb连接:
bash复制arm-linux-gnueabi-gdb vmlinux
(gdb) target remote localhost:1234
默认的根文件系统可能包含不必要的组件,可以通过以下方式优化:
这套QEMU模拟环境可以用于:
虽然QEMU模拟环境功能强大,但在实际生产部署时还需注意:
建议将QEMU作为开发和早期测试工具,最终发布前在真实硬件上进行全面验证。