概述
QEMU(Quick EMUlator)是一款通用的开源模拟器和虚拟化工具,它可以模拟绝大多数的CPU平台,比如x86、ARM、ARM64、RISC-V、PowerPC、MIPS等等,还能模拟各种硬件外设,如内存、emmc、sdcard、usb等等,所以通过qemu就能模拟出一块开发板。芯片公司研发芯片过程中,在芯片回片之前,一般都会先通过qemu来做前期的软件开发调试工作。这篇文章我们介绍如何通过qemu模拟ARM64平台并跑起来linux kernel,进而方便内核开发和调试。
这篇文章的涉及到的工具和源码的版本信息如下:
Ubuntu: Ubuntu 22.04.2 LTSqemu: 8.2.0 kernel: Linux 6.1.72 busybox: busybox-1.36.1
如果您当前还没有ubuntu的开发环境,可以参考 VMware Workstation安装Ubuntu-22.04虚拟机 这篇教程来安装ubuntu系统。
编译qemu模拟器
安装工具
安装编译qemu源码需要用到的工具,命令和日志如下:
max@ubuntu2204:~$ sudo apt update [sudo] password for max: Hit:1 http://mirrors.tuna.tsinghua.edu.cn/ubuntu jammy InRelease Hit:2 http://mirrors.tuna.tsinghua.edu.cn/ubuntu jammy-updates InRelease …… …… Building dependency tree... Done Reading state information... Done 407 packages can be upgraded. Run 'apt list --upgradable' to see them. max@ubuntu2204:~$ max@ubuntu2204:~$ sudo apt -y install python3.10-venv python3-sphinx ninja-build libpixman-1-dev pkg-config flex bison libfdt-dev libglib2.0-dev Reading package lists... Done Building dependency tree... Done Reading state information... Done The following additional packages will be installed: …… …… Setting up python3-docutils (0.17.1+dfsg-2) ... Setting up python3-sphinx (4.3.2-1) ... max@ubuntu2204:~$
下载qemu源码
qemu模拟器的官网地址为https://www.qemu.org/,主页如下:
在qemu官网download页面查看qemu发布的所有的版本源码包,如下:
通过wget下载qemu模拟器的源码,命令和日志如下:
max@ubuntu2204:~$ wget https://download.qemu.org/qemu-8.2.0.tar.xz --2024-01-13 21:46:41-- https://download.qemu.org/qemu-8.2.0.tar.xz Resolving download.qemu.org (download.qemu.org)... 143.244.51.238, 89.187.187.12, 185.180.13.211, ... …… …… qemu-8.2.0.tar.xz 100%[===============================================================================================================>] 123.99M 2.54MB/s in 58s 2024-01-13 21:47:42 (2.14 MB/s) - ‘qemu-8.2.0.tar.xz’ saved [130008888/130008888] max@ubuntu2204:~$
编译qemu模拟器
解压下载的qemu-8.2.0.tar.xz压缩包,并编译qemu源码,命令和日志如下:
max@ubuntu2204:~$ tar xvJf qemu-8.2.0.tar.xz qemu-8.2.0/ qemu-8.2.0/.patchew.yml qemu-8.2.0/ebpf/ …… …… qemu-8.2.0/subprojects/libvhost-user/libvhost-user-glib.h qemu-8.2.0/subprojects/keycodemapdb.wrap qemu-8.2.0/qemu.nsi max@ubuntu2204:~$ max@ubuntu2204:~$ cd qemu-8.2.0 max@ubuntu2204:~/qemu-8.2.0$ max@ubuntu2204:~/qemu-8.2.0$ ./configure Using './build' as the directory for build output python determined to be '/usr/bin/python3' python version: Python 3.10.12 …… …… Found ninja-1.10.1 at /usr/bin/ninja Running postconf script '/home/max/qemu-8.2.0/build/pyvenv/bin/python3 /home/max/qemu-8.2.0/scripts/symlink-install-tree.py' max@ubuntu2204:~/qemu-8.2.0$ max@ubuntu2204:~/qemu-8.2.0$ make -j8 changing dir to build for make ""... make[1]: Entering directory '/home/max/qemu-8.2.0/build' ninja: no work to do. /home/max/qemu-8.2.0/build/pyvenv/bin/meson introspect --targets --tests --benchmarks | /home/max/qemu-8.2.0/build/pyvenv/bin/python3 -B scripts/mtest2make.py > Makefile.mtest …… …… [9342/9342] Linking target tests/qtest/dbus-display-test make[1]: Leaving directory '/home/max/qemu-8.2.0/build' max@ubuntu2204:~/qemu-8.2.0$
上面命令会把qemu支持的所有平台的模拟器都编译出来,耗时会较长。如果想要节省编译时间,在执行configure命令时可以通过配置 target-list参数来选择只编译aarch64的模拟器,如下:
./configure --target-list=aarch64-softmmu,aarch64-linux-user,aarch64_be-linux-user --enable-kvm --enable-debug
到此qemu模拟器就编译完成了,查看生成的各个平台的模拟器,如下:
max@ubuntu2204:~/qemu-8.2.0$ ls build/qemu-system-* build/qemu-system-aarch64 build/qemu-system-hppa build/qemu-system-microblazeel build/qemu-system-nios2 build/qemu-system-riscv64 build/qemu-system-sparc build/qemu-system-xtensaeb build/qemu-system-alpha build/qemu-system-i386 build/qemu-system-mips build/qemu-system-or1k build/qemu-system-rx build/qemu-system-sparc64 build/qemu-system-arm build/qemu-system-loongarch64 build/qemu-system-mips64 build/qemu-system-ppc build/qemu-system-s390x build/qemu-system-tricore build/qemu-system-avr build/qemu-system-m68k build/qemu-system-mips64el build/qemu-system-ppc64 build/qemu-system-sh4 build/qemu-system-x86_64 build/qemu-system-cris build/qemu-system-microblaze build/qemu-system-mipsel build/qemu-system-riscv32 build/qemu-system-sh4eb build/qemu-system-xtensa
后面就可以通过 build/qemu-system-aarch64 模拟arm64平台处理器来运行Linux kernel了。
安装arm64交叉编译工具链
当前我们选择用linaro的交叉编译工具链来编译uboot、kernel、busybox等,linaro交叉编译工具链的下载网址为:https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/,网页如下:
我们先创建一个~/cross-compiler目录,然后在该目录下下载交叉编译工具链压缩包gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz 并解压,命令和日志如下:
max@ubuntu2204:~$ mkdir ~/cross-compiler && cd ~/cross-compiler max@ubuntu2204:~/cross-compiler$ wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz --2024-01-14 00:07:24-- https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz Resolving releases.linaro.org (releases.linaro.org)... 52.215.200.125 …… …… gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu. 100%[===============================================================================================================>] 112.43M 1.13MB/s in 4m 37s 2024-01-14 00:12:04 (416 KB/s) - ‘gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz’ saved [117896452/117896452] max@ubuntu2204:~/cross-compiler$ max@ubuntu2204:~/cross-compiler$ tar xf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz max@ubuntu2204:~/cross-compiler$
把该交叉编译工具链的bin目录添加到 PATH 环境变量中,如下:
PATH=/home/max/cross-compiler/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
这样我们就可以使用这个交叉编译工具链了,如下:
max@ubuntu2204:~/cross-compiler$ aarch64-linux-gnu-gcc -v Using built-in specs. COLLECT_GCC=aarch64-linux-gnu-gcc COLLECT_LTO_WRAPPER=/home/max/cross-compiler/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/../libexec/gcc/aarch64-linux-gnu/7.5.0/lto-wrapper …… …… Thread model: posix gcc version 7.5.0 (Linaro GCC 7.5-2019.12) max@ubuntu2204:~/cross-compiler$
到此arm64交叉编译工具链就安装完成了。
编译linux kernel
安装工具
在ubuntu上安装编译内核需要用到的工具,命令和日志如下:
max@ubuntu2204:~$ sudo apt install -y build-essential libncurses-dev bison flex libssl-dev libelf-dev git Reading package lists... Done Building dependency tree... Done Reading state information... Done …… …… Setting up build-essential (12.9ubuntu3) ... Processing triggers for man-db (2.10.2-1) ... Processing triggers for libc-bin (2.35-0ubuntu3.1) ... max@ubuntu2204:~$
下载linux内核源码
我们这里下载的是linux官方最新的longterm版本linux-6.1.y 的内核,从linux官网下载源码耗时较长,所以我们选择从清华仓库中下载。大家可以参考 获取Linux内核源码 这篇文章来查看linux官方发布的所有版本和各种快速下载内核源码方法。下载命令和日志如下:
max@ubuntu2204:~$ git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git -b linux-6.1.y Cloning into 'linux-stable'... remote: Enumerating objects: 12104695, done. Receiving objects: 100% (12104695/12104695), 2.58 GiB | 6.23 MiB/s, done. remote: Total 12104695 (delta 0), reused 0 (delta 0), pack-reused 12104695 Resolving deltas: 100% (10399156/10399156), done. Checking objects: 100% (33554432/33554432), done. Updating files: 100% (78744/78744), done. max@ubuntu2204:~$
进入linux-stable目录查看下载的内核的版本为Linux 6.1.72,命令和日志如下:
max@ubuntu2204:~$ cd linux-stable/ max@ubuntu2204:~/linux-stable$ git branch -vv * linux-6.1.y 7c58bfa711cb [origin/linux-6.1.y] Linux 6.1.72 max@ubuntu2204:~/linux-stable$
编译linux内核源码
进入到linux-stable 内核目录下,通过menuconfig配置内核选项,命令和日志如下:
max@ubuntu2204:~/linux-stable$ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 O=build menuconfig make[1]: Entering directory '/home/max/linux-stable/build' GEN Makefile # # using defaults found in arch/arm64/configs/defconfig # *** End of the configuration. *** Execute 'make' to start the build or try 'make help'. make[1]: Leaving directory '/home/max/linux-stable/build' max@ubuntu2204:~/linux-stable$
执行上面的命令会进入到menuconfig 菜单页面,如下:
在菜单中依次选择如下选项:
Device Drivers> Block devices > RAM block device support
并且把 “Default RAM disk size (kbytes)”调整为65536,调整后界面如下:
然后选择save保存,并退出menuconfig菜单,到此内核选项就配置完成了。下面开始编译内核镜像,命令和日志如下:
max@ubuntu2204:~/linux-stable$ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 O=build -j8 make[1]: Entering directory '/home/max/linux-stable/build' SYNC include/config/auto.conf.cmd GEN Makefile …… …… LD [M] sound/soc/tegra/snd-soc-tegra210-sfc.ko make[1]: Leaving directory '/home/max/linux-stable/build' max@ubuntu2204:~/linux-stable$
查看生成的Image内核镜像,命令和日志如下:
max@ubuntu2204:~/linux-stable$ file build/arch/arm64/boot/Image build/arch/arm64/boot/Image: Linux kernel ARM64 boot executable Image, little-endian, 4K pages max@ubuntu2204:~/linux-stable$
到此内核镜像Image就编译完成了。
编译busybox
Busybox的官方网页为https://busybox.net/ ,我们通过wget下载主页上的BusyBox 1.36.1版本的源码,命令和日志如下:
max@ubuntu2204:~$ wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2 --2024-01-14 11:50:58-- https://busybox.net/downloads/busybox-1.36.1.tar.bz2 Resolving busybox.net (busybox.net)... 140.211.167.122 …… …… busybox-1.36.1.tar.bz2 100%[================================================================================>] 2.41M 1.11MB/s in 2.2s 2024-01-14 11:51:15 (1.11 MB/s) - ‘busybox-1.36.1.tar.bz2’ saved [2525473/2525473] max@ubuntu2204:~$
解压busybox源码并进入源码目录,然后通过menuconfig菜单选择静态编译,即选择“Build static binary (no shared libs)”选项,命令和日志如下:
max@ubuntu2204:~$ tar xf busybox-1.36.1.tar.bz2 max@ubuntu2204:~$ cd busybox-1.36.1/ max@ubuntu2204:~/busybox-1.36.1$ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 menuconfig HOSTCC scripts/basic/fixdep HOSTCC scripts/basic/split-include …… …… *** End of configuration. *** Execute 'make' to build the project or try 'make help'. max@ubuntu2204:~/busybox-1.36.1$
执行上面的命令,会显示出busybox的menuconfig菜单,如下:
在菜单中依次选择:
Settings> [*] Build static binary (no shared libs)
界面如下:
然后保存退出,到此busybox的选项就配置完成了。下面开始编译busybox,命令和日志如下:
max@ubuntu2204:~/busybox-1.36.1$ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 install -j8 SPLIT include/autoconf.h -> include/config/* GEN include/bbconfigopts.h GEN include/common_bufsiz.h …… …… -------------------------------------------------- You will probably need to make your busybox binary setuid root to ensure all configured applets will work properly. --------------------------------------------------
查看生成的busybox可执行文件,命令如下:???????
max@ubuntu2204:~/busybox-1.36.1$ file _install/bin/busybox _install/bin/busybox: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), stati
制作 initrd 文件系统镜像
为了方便制作initrd文件系统镜像,我们通过mk_initrd.sh脚本来生成initrd.ext4镜像文件,后面在内核跑起来后会挂载该镜像中的文件系统,脚本如下:
#!/bin/bash set -x CURR_DIR=`pwd` MOUNT_DIR=$CURR_DIR/mount_point BUSYBOX_PREFIX=/home/max/busybox-1.36.1 dd if=/dev/zero of=initrd.ext4 bs=1M count=32 mkfs.ext4 initrd.ext4 mkdir -p $MOUNT_DIR sudo mount initrd.ext4 $MOUNT_DIR cp -arf $BUSYBOX_PREFIX/_install/* $MOUNT_DIR cd $MOUNT_DIR mkdir -p etc dev mnt proc sys tmp mnt etc/init.d/ echo "proc /proc proc defaults 0 0" > etc/fstab echo "tmpfs /tmp tmpfs defaults 0 0" >> etc/fstab echo "sysfs /sys sysfs defaults 0 0" >> etc/fstab echo "#!/bin/sh" > etc/init.d/rcS echo "mount -a" >> etc/init.d/rcS echo "mount -o remount,rw /" >> etc/init.d/rcS echo "echo -e "Welcome to ARM64 Linux"" >> etc/init.d/rcS chmod 755 etc/init.d/rcS echo "::sysinit:/etc/init.d/rcS" > etc/inittab echo "::respawn:-/bin/sh" >> etc/inittab echo "::askfirst:-/bin/sh" >> etc/inittab chmod 755 etc/inittab cd dev mknod console c 5 1 mknod null c 1 3 mknod tty1 c 4 1 cd $CURR_DIR sudo umount $MOUNT_DIR echo "make initrd ok!"
用户需要根据自己编译的busybox所在目录来调整脚本中的 BUSYBOX_PREFIX 变量。修改完mk_initrd.sh 脚本后,我们先创建一个make_initrd目录,并把 mk_initrd.sh 脚本拷贝到该目录下,由于在ubuntu下做mount和umount操作需要sudo权限,所以在执行mk_initrd.sh脚本前需要先在终端进入sudo权限,然后再执行该 mk_initrd.sh 脚本生成initrd.ext4镜像,命令和日志如下:???????
max@ubuntu2204:~$ mkdir make_initrd max@ubuntu2204:~$ cd make_initrd/ max@ubuntu2204:~/make_initrd$ ls mk_initrd.sh max@ubuntu2204:~/make_initrd$ max@ubuntu2204:~/make_initrd$ sudo su [sudo] password for max: root@ubuntu2204:/home/max/make_initrd# root@ubuntu2204:/home/max/make_initrd# sh mk_initrd.sh + pwd + CURR_DIR=/home/max/make_initrd + MOUNT_DIR=/home/max/make_initrd/mount_point + BUSYBOX_PREFIX=/home/max/busybox-1.36.1 …… …… + echo make initrd ok! make initrd ok! root@ubuntu2204:/home/max/make_initrd# root@ubuntu2204:/home/max/make_initrd# exit exit max@ubuntu2204:~/make_initrd$ ls -lh initrd.ext4 -rw-r--r-- 1 root root 32M 1月 14 12:32 initrd.ext4
到此文件系统镜像initrd.ext4就做好了。
使用qemu aarch64模拟器运行Linux内核
首先查看当前qemu支持哪些ARM 64位的CPU,命令和日志如下:???????
max@ubuntu2204:~/qemu-8.2.0/build/aarch64-softmmu$ ./qemu-system-aarch64 -cpu help Available CPUs: a64fx arm1026 arm1136 …… …… sa1100 sa1110 ti925t max@ubuntu2204:~/qemu-8.2.0/build/aarch64-softmmu$
这里我们选择用ARMv8的64位CPU cortex-a57来运行linux内核,由于命令参数过长,方便起见,我们把运行qemu模拟器的命令写到一个脚本qemu.sh中,脚本如下:???????
#!/bin/bash set -x QEMU=~/qemu-8.2.0 KERNEL=~/linux-stable INITRD=~/make_initrd $QEMU/build/aarch64-softmmu/qemu-system-aarch64 -nographic -M virt -cpu cortex-a57 -smp 2 -m 4G -kernel $KERNEL/build/arch/arm64/boot/Image -append "nokaslr root=/dev/ram init=/linuxrc console=ttyAMA0 console=ttyS0" -initrd $INITRD/initrd.ext4
用户需要根据自己编译的qemu、kernel和initrd.ext4镜像所在的目录调整qemu.sh脚本中的QEMU、KERNEL、INITRD三个变量。然后通过qemu.sh脚本来让模拟器运行linux内核,对应的命令和日志如下:???????
max@ubuntu2204:~$ sh qemu.sh + QEMU=/home/max/qemu-8.2.0 + KERNEL=/home/max/linux-stable + INITRD=/home/max/make_initrd + /home/max/qemu-8.2.0/build/aarch64-softmmu/qemu-system-aarch64 -nographic -M virt -cpu cortex-a57 -smp 2 -m 4G -kernel /home/max/linux-stable/build/arch/arm64/boot/Image -append nokaslr root=/dev/ram init=/linuxrc console=ttyAMA0 console=ttyS0 -initrd /home/max/make_initrd/initrd.ext4 [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x411fd070] [ 0.000000] Linux version 6.1.72 (max@ubuntu2204) (aarch64-linux-gnu-gcc (Linaro GCC 7.5-2019.12) 7.5.0, GNU ld (Linaro_Binutils-2019.12) 2.28.2.20170706) #1 SMP PREEMPT Sun Jan 14 11:57:15 CST 2024 …… …… [ 1.739471] Freeing unused kernel memory: 7552K [ 1.740835] Run /linuxrc as init process [ 1.865209] EXT4-fs (ram0): re-mounted. Quota mode: none. Welcome to ARM64 Linux Please press Enter to activate this console. ~ #
到此我们就用qemu aarch64的模拟器跑起来linux内核了,并挂载上了文件系统。此时我们就能输入命令控制模拟器中的linux系统了,比如:???????
~ # cat /proc/version Linux version 6.1.72 (max@ubuntu2204) (aarch64-linux-gnu-gcc (Linaro GCC 7.5-2019.12) 7.5.0, GNU ld (Linaro_Binutils-2019.12) 2.28.2.20170706) #1 SMP PREEMPT Sun Jan 14 11:57:15 CST 2024 ~ #
如果要退出qemu模拟器的系统,需要先按下ctrl+a,松开后,再按x,这样就退出qemu模拟器的系统了。
到此我们就成功通过qemu aarch64模拟器跑起来linux内核了!
请关注微信公众号 “Linux研习社” 获取更多技术内容: