QEMU编译安装及Linux系统仿真
概述
QEMU是一个通用和开源计算机仿真器和虚拟机,支持:
- 全系统仿真:在任何受支持的体系结构上运行任何计算机的操作系统;
- 用户模式仿真:在任何受支持的体系结构上运行另一个 Linux/BSD 目标的程序;
- 虚拟化:以接近本机的性能运行 KVM 和 Xen 虚拟机;
当用作仿真器时,QEMU可以在一台机器(如x86 PC)上运行为另一台机器(如ARM开发板)制作的操作系统和程序。
在进行嵌入式Linux系统学习时,QEMU一定程度上
帮我们摆脱了硬件限制,前期的学习和验证都可以基于QEMU仿真器进行,让我们摆脱了因为没有开发板,无法进行学习的困扰。
本文记录了使用QEMU仿真Arm开发板的过程。
QEMU编译安装
系统自带的qemu版本过于老旧,需要下载源码进行编译安装。
源码下载:qemu-6.2.0
编译(Ubuntu18.04):
# 解压
tar xf qemu-6.2.0.tar.xz
mkdir -p build
cd build
# 编译配置, 可通过 ../qemu-6.2.0/configure --help 查看帮助信息
../qemu-6.2.0/configure --prefix=$HOME/.local --target-list=aarch64-softmmu,arm-softmmu,riscv32-softmmu,riscv64-softmmu,aarch64-linux-user,arm-linux-user,riscv32-linux-user,riscv64-linux-user --static
# 安装编译依赖: 如果提示还有其他依赖未安装, 通过 apt search 查看对应的依赖并安装
sudo apt-get install -y ninja-build libpixman-1-dev
# 编译
make && make install
验证QEMU:
# 如果没有将 $HOME/.local/bin 添加到环境变量, 则添加
export PATH=$HOME/.local/bin:$PATH
正常安装完成有如下产物:
通过 qemu-system-arm --help
查看帮助信息,通过 qemu-system-arm -cpu help
可以查看支持的CPU,通过 qemu-system-arm -machine help
可以查看支持开发板。例如:
qemu-system-arm -machine help
Supported machines are:
... ...
mcimx6ul-evk Freescale i.MX6UL Evaluation Kit (Cortex-A7)
... ...
vexpress-a9 ARM Versatile Express for Cortex-A9
... ...
qemu-system-aarch64 -machine help
Supported machines are:
... ...
raspi3b Raspberry Pi 3B (revision 1.2)
... ...
准备根文件系统
制作过程见 使用BusyBox制作Linux根文件系统 。
制作根文件系统:
dd if=/dev/zero of=rootfs.ext4 bs=1M count=64
mkfs.ext4 rootfs.ext4
sudo mount rootfs.ext4 /mnt/
sudo cp -af rootfs/* /mnt/
sudo mknod /mnt/dev/null c 1 3
sudo mknod /mnt/dev/tty1 c 4 1
sudo mknod /mnt/dev/tty2 c 4 2
sudo mknod /mnt/dev/tty3 c 4 3
sudo mknod /mnt/dev/tty4 c 4 4
sudo mknod /mnt/dev/tty5 c 4 5
sudo mknod /mnt/dev/console c 5 1
sudo umount /mnt
产物 rootfs.ext4
存放在 $HOME
目录下,后续会使用到。
Linux内核编译
环境准备
源码下载:
git clone https://source.codeaurora.org/external/imx/linux-imx
git checkout rel_imx_4.9.88_2.0.0_ga # 这是一个tag
此时仓库状态:HEAD detached at rel_imx_4.9.88_2.0.0_ga
。tag 相当于是一个快照,不能更改它的代码,如果要在该tag的基础上做修改,可以基于该tag开新的分支(git checkout -b $branch_name $tag_name
)。
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
本文的目标是在imx6ull上运行,在此之前,先按大多数教程试用和验证一下 QEMU编译安装 小节生成的qemu程序。
vexpress
编译内核:
make vexpress_defconfig
# 直接编会链接不过, 需要 make menuconfig 到 > Device Drivers > Input device support 打开 Polled input device skeleton
make -j4
第一次尝试:
qemu-system-arm -nographic -machine vexpress-a9 -m 512M -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -append "console=ttyAMA0 loglevel=8"
此时报错:
---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
原因是没有指定根文件系统。
第二次尝试:
qemu-system-arm -nographic -machine vexpress-a9 -m 512M -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -append "console=ttyAMA0 root=/dev/mmcblk0 rootfstype=ext4 loglevel=8" -drive file=~/rootfs.ext4,format=raw,id=sdcard -device sd-card,drive=sdcard
此时能正常进入系统:
Booting Linux on physical CPU 0x0
Linux version 4.9.88+ (***@ubuntu18.04) (gcc version 7.5.0 (Linaro GCC 7.5-2019.12) ) #2 SMP Sun Aug 28 07:50:20 CST 2022
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c53c7d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt:Machine model: V2P-CA9
... ...
EXT4-fs (mmcblk0): mounted filesystem with ordered data mode. Opts: (null)
VFS: Mounted root (ext4 filesystem) readonly on device 179:0.
Freeing unused kernel memory: 1024K
random: crng init done
Please press Enter to activate this console.
Processing /etc/profile... Done
/ #
按 Ctrl+a x
退出QEMU仿真。
imx6ull
选择合适的编译配置(imx_v7_defconfig
):
编译内核:
make distclean
make imx_v7_defconfig
make -j4
第一次尝试(需要修改 machine
,dtb
和 append
参数):
qemu-system-arm -nographic -machine mcimx6ul-evk -m 512M -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/imx6ull-14x14-evk.dtb -append "console=ttymxc0,115200 root=/dev/mmcblk0 rootfstype=ext4 loglevel=8" -drive file=~/rootfs.ext4,format=raw,id=sdcard -device sd-card,drive=sdcard
现象:没有任何打印
,QEMU没有起来。
【尝试1】:修改打印等级和打印端口:
- 进入
> Kernel hacking > printk and dmesg options
;- 使能
Show timing information on printks
,将Default message log level (1-7)
修改为 7;- 使能
Kernel low-level debugging functions
,将Kernel low-level debugging port
修改为 i.MX6UL Debug UART(作用点);
重新编译运行,现象:
Uncompressing Linux... done, booting the kernel.
然后卡住。
【尝试2】:使用gdb查看运行到什么地方:
- 进入
> Kernel hacking > Compile-time checks and compiler options
;- 使能
Compile the kernel with debug info
,使能Provide GDB scripts for kernel debugging
;
重新编译,QEMU带调试参数(-S -s
)运行,新建窗口进行GDB调试:
qemu-system-arm -nographic -machine mcimx6ul-evk -m 512M -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/imx6ull-14x14-evk.dtb -append "console=ttymxc0,115200 root=/dev/mmcblk0 rootfstype=ext4 loglevel=8" -drive file=~/rootfs.ext4,format=raw,id=sdcard -device sd-card,drive=sdcard -S -s
在另一个窗口使用 arm-linux-gnueabihf-gdb vmlinux
调试:
(gdb) target remote :1234
Remote debugging using :1234
0x80000000 in ?? ()
(gdb) b start_kernel
Breakpoint 1 at 0x80e008d0: file init/main.c, line 486.
(gdb) c
Continuing.
Breakpoint 1, start_kernel () at init/main.c:486
486 set_task_stack_end_magic(&init_task);
(gdb) n
一路回车,发现重新会执行到:pr_notice("%s", linux_banner);
和 pr_notice("Kernel command line: %s\n", boot_command_line);
,按理应该有打印,但QEMU仿真窗口仍然没有新的打印出现,初步猜测是打印相关的编译配置和设备树没有配对。
此处问题定位卡住很长时间,暂时先使用现成的方案,后续计划进行解决。
【尝试3】:更换编译配置和设备树:
替换 百问网 用于QEMU仿真的编译配置和设备树,并进行适配:
100ask_imx6ull_qemu_defconfig
–> arch/arm/configs/imx6ull_qemu_defconfig
;
100ask_imx6ull_qemu.dts
–> arch/arm/boot/dts/imx6ull-qemu.dts
;
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index a27f48966938..5d5aa6ee73b5 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -524,6 +524,7 @@ dtb-$(CONFIG_SOC_IMX6ULL) += \
imx6ull-14x14-ddr3-arm2-uart2.dtb \
imx6ull-14x14-ddr3-arm2-usb.dtb \
imx6ull-14x14-ddr3-arm2-wm8958.dtb \
+ imx6ull-qemu.dtb \
imx6ull-14x14-evk.dtb \
imx6ull-14x14-evk-btwifi.dtb \
imx6ull-14x14-evk-btwifi-oob.dtb \
diff --git a/arch/arm/boot/dts/imx6ull-qemu.dts b/arch/arm/boot/dts/imx6ull-qemu.dts
index 9782aeb1bfef..d83bdafd71bd 100644
--- a/arch/arm/boot/dts/imx6ull-qemu.dts
+++ b/arch/arm/boot/dts/imx6ull-qemu.dts
@@ -133,10 +133,6 @@
assigned-clock-rates = <786432000>;
};
-&qemu_net {
- status = "okay";
-};
-
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1>;
继续编译验证:
make distclean
make imx6ull_qemu_defconfig
make -j4
qemu-system-arm -nographic -machine mcimx6ul-evk -m 512M -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/imx6ull-qemu.dtb -append "console=ttymxc0,115200 root=/dev/mmcblk0 rootfstype=ext4 loglevel=8" -drive file=~/rootfs.ext4,format=raw,id=sdcard -device sd-card,drive=sdcard
查看启动日志:
[ 1.176928] mmc1: host does not support reading read-only switch, assuming write-enable
[ 1.186928] mmc1: new high speed SD card at address 4567
[ 1.206928] mmcblk1: mmc1:4567 QEMU! 64.0 MiB
... ...
[ 1.427380] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[ 1.433212] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
修改 append
参数,继续仿真:
qemu-system-arm -nographic -machine mcimx6ul-evk -m 512M -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/imx6ull-qemu.dtb -append "console=ttymxc0,115200 root=/dev/mmcblk1 rootfstype=ext4 loglevel=8" -drive file=~/rootfs.ext4,format=raw,id=sdcard -device sd-card,drive=sdcard
此时能正常进入系统:
Uncompressing Linux... done, booting the kernel.
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Linux version 4.9.88-04763-g5e23f9d61147-dirty (***@ubuntu18.04) (gcc version 7.5.0 (Linaro GCC 7.5-2019.12) ) #1 SMP PREEMPT Sun Aug 28 10:35:52 CST 2022
[ 0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=30c53c7d
... ...
[ 2.659779] EXT4-fs (mmcblk1): mounted filesystem with ordered data mode. Opts: (null)
[ 2.669779] VFS: Mounted root (ext4 filesystem) readonly on device 179:0.
[ 2.688896] devtmpfs: mounted
[ 2.769778] Freeing unused kernel memory: 2048K
Please press Enter to activate this console.
Processing /etc/profile... Done
/ #
到此,本次仿真基本结束,接下来可以根据需要修改内核源码,脱离硬件限制,愉快地开始学习Linux内核吧 ^_^
。
后续计划
- Visual Studio Code + GDB + QEMU 图形化调试Linux内核;
done
- IMX6ULL QEMU仿真编译配置和设备树研究;