QEMU搭建ARM LINUX开发环境
QEMU简介:
QEMU是一种纯软件实现的虚拟化模拟器,几乎可以模拟所有硬件,包括我们本次要用的ARM A9平台。它的原理是将guest架构代码转换为TCG中间代码,再转换为host架构代码。
环境:
虚拟机:ubuntu18.04 LTS
步骤:
- 安装gcc-arm-linux-gnueabi
sudo apt install gcc-arm-linux-gnueabi
安装完成后,我们可以看一下该工具链支持的目标架构:
arm-linux-gnueabi-gcc --target
翻一下结果可以看到:
Known ARM CPUs (for use with the -mcpu= and -mtune= options):
arm1020e arm1020t arm1022e arm1026ej-s arm10e arm10tdmi arm1136j-s
arm1136jf-s arm1156t2-s arm1156t2f-s arm1176jz-s arm1176jzf-s arm2 arm250
arm3 arm6 arm60 arm600 arm610 arm620 arm7 arm70 arm700 arm700i arm710
arm7100 arm710c arm710t arm720 arm720t arm740t arm7500 arm7500fe arm7d
arm7di arm7dm arm7dmi arm7m arm7tdmi arm7tdmi-s arm8 arm810 arm9 arm920
arm920t arm922t arm926ej-s arm940t arm946e-s arm966e-s arm968e-s arm9e
arm9tdmi cortex-a12 cortex-a15 cortex-a15.cortex-a7 cortex-a17
cortex-a17.cortex-a7 cortex-a32 cortex-a35 cortex-a5 cortex-a53 cortex-a57
cortex-a57.cortex-a53 cortex-a7 cortex-a72 cortex-a72.cortex-a53 cortex-a73
cortex-a73.cortex-a35 cortex-a73.cortex-a53 cortex-a8 cortex-a9 cortex-m0
cortex-m0.small-multiply cortex-m0plus cortex-m0plus.small-multiply
cortex-m1 cortex-m1.small-multiply cortex-m23 cortex-m3 cortex-m33 cortex-m4
cortex-m7 cortex-r4 cortex-r4f cortex-r5 cortex-r7 cortex-r8 ep9312
exynos-m1 fa526 fa606te fa626 fa626te fa726te fmp626 generic-armv7-a iwmmxt
iwmmxt2 marvell-pj4 mpcore mpcorenovfp native strongarm strongarm110
strongarm1100 strongarm1110 xgene1 xscale
支持的arch和CPU非常全,也支持我们这次将要模拟的cortex-a9;甚至也支持cortex-m,也就可以编译无MMU的单片机的内核和应用。
- 安装qemu快捷安装如下,或者使用源码安装。
sudo apt install qemu
- 下载内核及busybox源码
下载主线源码: http://cdn.kernel.org/pub/linux/kernel/v4.x
下载busybox:https://busybox.net/downloads/busybox-1.31.1.tar.bz2 - 编译busybox
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install
至此,在busybox目录下生成了'_intall' 目录,将作为我们构建根文件系统的目录,在根文件系统目录下补充一些内容。
增加以下目录:
- etc :主要存放一些配置文件如:inittab(init进程会解析此文件,看进一步动作);fstab(主要包含一些挂载的文件系统,如sys proc) init.rd/rcS(可存放一些可执行脚本,配合inittab使用)
- proc : proc文件系统挂载点
- sys : sys文件系统挂载点
- tmp : tmp文件系统挂载点
- dev : 设备文件
- lib : 库文件目录(如果busybox采用动态链接库,则需要将交叉编译链的库文件拷这里)
a. dev目录下静态创建如下节点:
sudo mknod -m 666 tty1 c 4 1
sudo mknod -m 666 tty2 c 4 2
sudo mknod -m 666 tty3 c 4 3
sudo mknod -m 666 tty4 c 4 4
sudo mknod -m 666 console c 5 1
sudo mknod -m 666 null c 1 3
console 和 null 是必须的,如果没有则会报错。
b. etc/inittab 文件内容如下,可参考busyboxdir/examples/inittab编写:
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
c. etc/fstab 文件内容如下,主要目的是指明一些文件系统挂载点:
#device mount-point type option dump fsck order
proc /proc proc defaults 0 0
temps /tmp rpoc defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
mdev /dev ramfs defaults 0 0
d. etc/init.d/rcS 文件内容如下,inittab第一条指明了从rcS中去执行脚本
mount -a
echo "/sbin/mdev" > /proc/sys/kernel/hotplug
/sbin/mdev -s # 根据/etc/mdev.conf中的配置进行生成设备节点
mount -a
顺便修改rcS的权限:
chmod 777 etc/init.d/rcS
e. lib 文件拷贝,因为busybox我们采用默认的动态链接(建议),这样可以节省根文件系统大小,因为应用也可以链接相应的库。首先通过下边3条命令任意一条查看busybox依赖的库文件。
arm-linux-readelf -d busybox | grep NEEDED
arm--linux-objdump -x busybox | grep NEEDED
strings busybox | grep ^lib
注意:ld-linux.so.3有时候不会显示,我们也必须拷贝它,如果以后编译应用程序,我们也要查看依赖的库,补足根文件系统中缺少的库文件。
cp /usr/arm-linux-gnueabi/lib/ld-linux.so.3 _install/lib/
cp /usr/arm-linux-gnueabi/lib/libc.so.6 _install/lib/
cp /usr/arm-linux-gnueabi/lib/libm.so.6 _install/lib/
cp /usr/arm-linux-gnueabi/lib/libresolv.so.2 _install/lib/
- 编译内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig(option)
make
将 zImage dtb文件拷贝出来,方便使用。如果已经我们已经开发了一些驱动,需要进行测试,除了将ko文件拷贝到根文件系统中,也应该执行模块安装命令,否则modules依赖关系,参数、符号等无法使用。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules_install INSTALL_MOD_PATH=busybox-dir/_install/
执行完成后,将在根文件系统的lib下创建modules目录。
- 创建根文件系统镜像文件
使用dd命令创建一个空白的32M(根据实际情况)文件:
dd if=/dev/zero of=rootfs.ext3 bs=1M count=32
将该空白文件格式化为ext3格式(内核默认支持文件系统,如果使用其他需要配置内核):
mkfs.ext3 rootfs.ext3
将该空白文件,挂载在一个目录下:
mkdir fs
sudo mount -o loop rootfs.ext3 ./fs
将busybox构建的根文件系统拷贝到挂载点下,然后再卸载
sudo cp -rf busybox-dir/_install/* ./fs
sudo umount ./fs
- 使用qemu运行,验证。
为了方便不每次都输一串命令,我们可以建立一个run.sh脚本,内容如下:
qemu-system-arm \
-M vexpress-a9 \
-kernel ./zImage \
-nographic \
-m 512M \
-smp 4 \
-sd ./rootfs.ext3 \
-dtb vexpress-v2p-ca9.dtb \
-append "init=/linuxrc root=/dev/mmcblk0 rw rootwait earlyprintk console=ttyAMA0"
上述脚本,-M 指定了目标板, -kernel 指定了linux内核镜像, -nographic 指定无图形显示,-m 512M指定了运行内存大小,-smp 指定4核, -sd 指定了外部有1个sd卡,卡内是rootfs.ext3镜像文件, -dtb 指定了设备树文件, -append 指定了bootargs; bootargs中init=/linuxrc 指定了init进程是根文件系统下的linuxrc(busybox生成), root=/dev/mmcblk0 指定了根文件系统为sd卡, console指定了ttyAMA0,即控制台。
运行结果:
Booting Linux on physical CPU 0x0
Linux version 4.19.86 (yhangzzz@yhangzzz-virtual-machine) (gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1)) #2 SMP Sun Dec 1 13:35:37 CST 2019
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
Memory policy: Data cache writealloc
random: get_random_bytes called from start_kernel+0x98/0x474 with crng_init=0
percpu: Embedded 16 pages/cpu s32780 r8192 d24564 u65536
Built 1 zonelists, mobility grouping on. Total pages: 130048
Kernel command line: init=/linuxrc root=/dev/mmcblk0 rw rootwait earlyprintk console=ttyAMA0
log_buf_len individual max cpu contribution: 4096 bytes
...
...
...
EXT4-fs (mmcblk0): mounting ext3 file system using the ext4 subsystem
random: fast init done
EXT4-fs (mmcblk0): recovery complete
EXT4-fs (mmcblk0): mounted filesystem with ordered data mode. Opts: (null)
VFS: Mounted root (ext3 filesystem) on device 179:0.
Freeing unused kernel memory: 1024K
Run /linuxrc as init process
random: crng init done
mount: mounting temps on /tmp failed: No such device
Please press Enter to activate this console.
/bin/sh: can't access tty; job control turned off
/ # ls
bin etc lib lost+found sbin tmp
dev home linuxrc proc sys usr
/ #
本文中,使用qemu搭建了arm-a9的虚拟化平台,用于linux内核开发、驱动开发、根文件系统构建、应用开发,或者uboot开发。虽然是虚拟的,但是和实际开发中的步骤基本一致。