使用qemu模拟器搭建arm运行环境

Finally,I got it!
首先,查资料自觉用google。恕我直言,除google外,其他的都是垃圾!

参考网上的教程,参考Google,基于自己的理解,终于走出了第一步,至此,记录下来,希望留给更多的人参考。

环境如下:

开发环境内核版本:

thorn@ubuntu:~$ cat /proc/version
Linux version 4.4.0-63-generic (buildd@lcy01-31) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #84-Ubuntu SMP Wed Feb 1 17:20:32 UTC 2017
thorn@ubuntu:~$ uname -a
Linux ubuntu 4.4.0-63-generic #84-Ubuntu SMP Wed Feb 1 17:20:32 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
thorn@ubuntu:~$

gcc版本:

thorn@ubuntu:~$ arm-linux-gnueabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/arm-linux-gnueabi/4.9/lto-wrapper
Target: arm-linux-gnueabi
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.9.3-13ubuntu2' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/arm-linux-gnueabi/include/c++/4.9.3 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libitm --disable-libquadmath --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-armel-cross/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-armel-cross --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-armel-cross --with-arch-directory=arm --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libgcj --enable-objc-gc --enable-multiarch --enable-multilib --disable-sjlj-exceptions --with-arch=armv5t --with-float=soft --disable-werror --enable-multilib --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabi --program-prefix=arm-linux-gnueabi- --includedir=/usr/arm-linux-gnueabi/include
Thread model: posix
gcc version 4.9.3 (Ubuntu/Linaro 4.9.3-13ubuntu2)

qemu版本:

git clone git://git.qemu-project.org/qemu.git
cd qemu
git checkout remotes/origin/stable-2.4 -b stable-2.4

linux源码内核版本:

wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.16.tar.xz

作为一个希望在嵌入式方向发展的同学,目前还没有真正的接触过嵌入式linux开发,很是惭愧,那么从模拟器开始吧。

总共花了2个多小时的时间,包括下载代码。由于对其中的某些过程还不是很理解,一路很是曲折。即便搭建起来了环境,还需要自己慢慢品味才行。

1. 首先下载Linux内核,以流行的3.16为例的原因是,作为一个初学者,希望有更多的参考资料。

两种方法:

与大多数的搭建过程不同,我选择了直接下载源码,原因有2:

  1. 以源码为基础,与大多数时候的开发环境相符,且更有成就感
  2. 因为是压缩包,源码的下载速度更快

内核下载完毕之后,解压。这些基本的操作大家都不会陌生。紧接下来的,安装arm的交叉编译链工具,这一步特别关键。很多参考资料直接给出了这样的命令行sudo apt-get install gcc-arm-linux-gnueabi,这是不严谨的,因为,随着gcc的更新,相关的工具链会做更新。
然而内核的编译过程却还是依赖着比较老的gcc工具链,这会导致编译过程中出现很多莫名其妙的问题,而这些问题,对于新手的成长弊大于利。
我在这个问题上面花了太多的时间,因为在编译的过程中总会出现莫名其妙的错误,改了一个,又出现另一个,如:

  1. fatal error: linux/compiler-gcc5.h: No such file or directory
  1. Makefile:901: recipe for target 'init' failed
  2. scripts/Makefile.build:390: recipe for target 'init/mounts.o' failed
  3. multiple definition of `return_address

可能要开始怀疑人生了。

对于每一个版本的内核,官方都会有推荐的工具链,版本不一定越高越好,匹配才行。
比如3.16的内核,gcc用4.9的是ok的,那么可能需要自己手动安装。
thorn@ubuntu:/usr/bin$ ls -l
找到下面我们需要的关键信息,即一个软链接文件。
lrwxrwxrwx 1 root root 34 Feb 27 21:56 arm-linux-gnueabi-gcc -> /usr/bin/arm-linux-gnueabi-gcc-5
这个文件表明我们使用的arm-linux-gnueabi-gcc 实际上是arm-linux-gnueabi-gcc-5,那么就需要自己手动安装:
sudo apt-get install gcc-4.9-arm-linux-gnueabi
sudo apt-get install gcc-4.9-arm-linux-gnueabi-base
由于不知道有何区别,索性都安装了下。

并不需要卸载老版本的gcc,安装之后,只需要自己手动更改默认的gcc版本:

  • 删除arm-linux-gnueabi-gcc,它只是个到/usr/bin/arm-linux-gnueabi-gcc-5的软链接
    thorn@ubuntu:/usr/bin$ sudo rm /usr/bin/arm-linux-gnueabi-gcc

  • 重新建立我们需要的软链接
    thorn@ubuntu:~/linux-3.16$ sudo ln -s /usr/bin/arm-linux-gnueabi-gcc-4.9 /usr/bin/arm-linux-gnueabi-gcc

  • 做过了这一步再编译,云开见日,以前莫名其妙的错误都消失了。

  • 那么thorn@ubuntu:/usr/bin$ ls -l后可以看到,gcc已经指向正确:
    lrwxrwxrwx 1 root root 34 Feb 27 21:56 arm-linux-gnueabi-gcc -> /usr/bin/arm-linux-gnueabi-gcc-4.9

  • 编译linux内核:
    生成vexpress开发板的config文件

make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm O=./out_vexpress_3_16 vexpress_defconfig

网上资料有如下步骤,据说是如果不做这个步骤,后面qemu会起不来。按照这个步骤,结果没有问题:

执行如下命令:
make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm O=./out_vexpress_3_16 menuconfig
将System Type -->的Enable the L2x0 outer cache controller 取消


实际上,经过测试,若不执行这一步,即按照默认的config来做,结果也ok。

编译:
make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm O=./out_vexpress_3_16 zImage -j2

编译OK,截图为证:

2. qemu模拟器的搭建(当然有更直接的方式,apt安装)

git clone git://git.qemu-project.org/qemu.git
cd qemu
git checkout remotes/origin/stable-2.4 -b stable-2.4

配置以前需要安装若干个软件包,惭愧的是自己并不知道这几个软件包的作用

sudo apt-get install zlib1g-dev libglib2.0-0 libglib2.0-dev libtool libtool libsdl1.2-dev autoconf

配置qemu,为了使qemu代码干净,中间文件都生成到build目录下

thorn@ubuntu:~/qemu$ mkdir build
thorn@ubuntu:~/qemu$ cd build/
thorn@ubuntu:~/qemu/build$ ../configure --target-list=arm-softmmu --audio-drv-list=

编译,安装

make
sudo make install

如果不是在root权限下,一定要加上sudo,比如我之前不知,就出现了如下的错误:

install -d -m 0755 "/usr/local/share/qemu"
cannot change permissions of ‘/usr/local/share/qemu’: No such file or directory

感谢google,我找到了这个error的解决办法。

测试qemu和内核能否运行成功
qemu已经安装好了,内核也编译成功了,到这里测试一下,编译出来的内核是否OK,或者qemu对vexpress单板支持是否够友好。
命令如下:

thorn@ubuntu:~/linux-3.16/out_vexpress_3_16$ qemu-system-arm -M vexpress-a9 -m 512M -kernel /home/thorn/linux-3.16/out_vexpress_3_16/arch/arm/boot/zImage -nographic -append "console=ttyAMA0"

如果看到内核启动过程中的打印,说明前的搭建是成功的。

-M vexpress-a9 模拟vexpress-a9单板,你可以使用-M ?参数来获取该qemu版本支持的所有单板
-m 512M 单板运行物理内存512M
-kernel /home/ivan/kernel_git/linux/arch/arm/boot/zImage 告诉qemu单板运行内核镜像路径
-nographic 不使用图形化界面,只使用串口
-append "console=ttyAMA0" 内核启动参数,这里告诉内核vexpress单板运行,串口设备是哪个tty。

以下命令杀死qemu-system-arm这个进程:

ps -A | grep qemu-system-arm | awk '{print $1}' | xargs sudo kill -9

以上就是本人arm运行环境的搭建过程,如有问题,欢迎评论,参考如下:

3. 制作根文件系统

你会注意到,有panic,我们还没有制作根文件系统

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

  • 下载、编译和安装busybox

wget http://www.busybox.NET/downloads/busybox-1.20.2.tar.bz2

thorn@ubuntu:~/busybox-1.20.2$ make defconfig
thorn@ubuntu:~/busybox-1.20.2$ make CROSS_COMPILE=arm-linux-gnueabi-
thorn@ubuntu:~/busybox-1.20.2$ make install CROSS_COMPILE=arm-linux-gnueabi-

安装完成后,会在busybox目录下生成_install目录,该目录下的程序就是单板运行所需要的命令。

  • 形成根目录结构
    先在Ubuntu主机环境下,形成目录结构,里面存放的文件和目录与单板上运行所需要的目录结构完全一样,然后再打包成镜像(在开发板看来就是SD卡),这个临时的目录结构称为根目录。

可以写一个脚本mkrootfs.sh完成这个任务

#!/bin/bash

sudo rm -rf rootfs
sudo rm -rf tmpfs
sudo rm -f a9rootfs.ext3

sudo mkdir rootfs
sudo cp ~/busybox-1.20.2/_install/*  rootfs/ -raf

sudo mkdir -p rootfs/proc/
sudo mkdir -p rootfs/sys/
sudo mkdir -p rootfs/tmp/
sudo mkdir -p rootfs/root/
sudo mkdir -p rootfs/var/
sudo mkdir -p rootfs/mnt/

sudo cp ~/etc rootfs/ -arf

sudo cp -arf /usr/arm-linux-gnueabi/lib rootfs/
sudo rm rootfs/lib/*.a
sudo arm-linux-gnueabi-strip rootfs/lib/*

sudo mkdir -p rootfs/dev/
sudo mknod rootfs/dev/tty1 c 4 1
sudo mknod rootfs/dev/tty2 c 4 2
sudo mknod rootfs/dev/tty3 c 4 3
sudo mknod rootfs/dev/tty4 c 4 4
sudo mknod rootfs/dev/console c 5 1
sudo mknod rootfs/dev/null c 1 3

sudo dd if=/dev/zero of=a9rootfs.ext3 bs=1M count=32
sudo mkfs.ext3 a9rootfs.ext3

sudo mkdir -p tmpfs
sudo mount -t ext3 a9rootfs.ext3 tmpfs/ -o loop
sudo cp -r rootfs/*  tmpfs/
sudo umount tmpfs

thorn@ubuntu:/$ sudo ./mkrootfs.sh由于在根目录下执行这个sh文件,故需要加上sudo。

接下来,就可以启动qemu来模拟vexpress开发板了,命令参数如下:

sudo qemu-system-arm \
    -M vexpress-a9 \
    -m 512M \
    -kernel /home/thorn/linux-3.16/out_vexpress_3_16/arch/arm/boot/zImage \
    -nographic \
    -append "root=/dev/mmcblk0  console=ttyAMA0" \
    -sd a9rootfs.ext3

由于要读写根目录下的文件,故,执行qemu时要加上sudo。

其中要注意的是,-sd a9rootfs.ext3,这个指的是绝对路径,由于我是在根目录下做这个的,所以,-sd后面直接是a9rootfs.ext3。

执行结果如下:


若是基于图形化的启动,如下:

sudo qemu-system-arm \
    -M vexpress-a9 \
    -serial stdio \
    -m 512M \
    -kernel /home/thorn/linux-3.16/out_vexpress_3_16/arch/arm/boot/zImage \
    -append "root=/dev/mmcblk0  console=ttyAMA0 console=tty0" \
    -sd a9rootfs.ext3

执行结果如下:


当然,后面的话,我们最好是把所有的文件都集成在一个大文件目录下,简洁清晰,而且,权限容易控制。
其中的~/etc为启动配置文件,后面可以基于此做一些个性化的启动配置。

推荐阅读更多精彩内容