目录
  1. 1. 开发环境
  2. 2. QEMU简介
  3. 3. Ubuntu安装qemu-system-arm
    1. 3.1. 系统提供的源安装:
    2. 3.2. 源文件编译安装:
  4. 4. qemu-system-arm启动uboot
  5. 5. uboot引导kernel+rootfs
    1. 5.1. tftp加载kernel
    2. 5.2. nfs加载rootfs
  6. 6. 总结
QEMU模拟AMRM-A9(tftp挂载kenel,nfs挂载rotfs)

初次接触linux系统是使用的2440的板子,跟着韦东山老师学习了uboot kernel 和文件系统,也算是入门了,后面又使用4412板子做了一些应用层的项目。但是底层的uboot和内核和文件系统都是在厂商的支持下,做的较为简单二次开发。已经毕业一个月了,业余时间太多,想好好学学驱动层的东西,但是苦于刚毕业,所以准备采用qemu模拟出arm的开发环境,这样我们只用关注软件层面。

开发环境

  • Ubuntu:Ubuntu 19.04
  • qemu: QEMU emulator version 3.1.0
  • arm交叉编译器:gcc version 4.8.3 20140320

QEMU简介

Ubuntu安装qemu-system-arm

这里有两种方法:

系统提供的源安装:

在ubuntu的Terminal下输入以下命令:

1
2
3
sudo apt-get install qemu
sudo apt-get install qemu-system-arm
安装完之后:qemu-system-arm -version 查看qemu版本

使用ubuntn16版本的时候,使用第一个命令安装就可以,但是使用Ubuntu19.04时,使用第一个命令安装完,搜索不到qemu-system-arm这个命令,还需要使用第二个命令安装。使用系统的源安装可以解决库依赖的问题。

源文件编译安装:

使用源文件安装我们需要使用git 将工程clone到本地(如果没有git的话,需要先去官网注册,然后在Ubuntu安装,配置等),安装之前还需要解决QEMU编译依赖的包,也可以在安装的过程中根据缺少的库依次安装。

1
2
3
4
下载源码:sudo git clone git://git.qemu.org/qemu.git
可以使用git命令切换到稳定版本:git checkout xxxx
编译配置 ./configure (可以使用 ./configure -help 查看支持的参数)
安装: make && make install (不出意外的话可以成功安装)

比较这个两种方法可以知道,第二种虽然比较复杂,但是灵活度更高,可以配置参数。第一种安装简单。

在启动uboot之前,我们还需要安装arm的交叉编译器,这里推荐不要用太老,也不要用太新的编译器(个人建议,以为有时候uboot kernel和文件系统会因为交叉编译器太老启动失败),可以直接去CodeSourcery Linaro 或者别的官网下载源码。

qemu-system-arm启动uboot

下载uboot源码:

1
2
1.在浏览器下载:https://ftp.denx.de/pub/u-boot/
2.wget命令下载:wget https://ftp.denx.de/pub/u-boot/u-boot-xxxxx.tar.bz2(版本自己选择)

检测arm交叉编译器是否安装成功:

1
arm-none-linux-gnueabi-gcc  -v

这里我自己编译一个简单的脚本 配置生成uboot

1
2
3
4
5
6
#!/bin/bash 
export ARCH=arm #指定编译生成架构 ,也可以在uboot根目录的Makefile修改
export CROSS_COMPILE=arm-none-linux-gnueabi- #指定编译生成的交叉编译链,也可以在uboot根目录的Makefile修改
make vexpress_ca9x4_defconfig #编译我们要生成平台u-boot.bin的配置文件,在uboot根目录下会生成.config文件(主要有点,ls -al 才能看到)。另外make menuconfig图形化配置也会最终修改.config文件里面的内容。也可以直接修改.config
source /etc/profile #我将交叉编译链的环境变量添加到profile文件里面。以防万一,让其生效
make #生成

执行成功之后会看到生成u-boot u-boot.bin等文件,使用以下命令可以核对生成文件的架f构:

1
file u-boot

qemu启动uboot脚本(注意给脚本权限)

1
2
3
4
5
6
 #!/bin/sh
qemu-system-arm \
-M vexpress-a9 \ #指定开发板
-m 512M \ #内存
-nographic \ #不使用LCD屏,即使用当前Terminal
-kernel /home/linux/armuboot/u-boot-2017.05/u-boot \

uboot有一个循环的命令解释器,在启动3秒内(时间可设置),按任意键即可进入。

可以在3秒内进入uboot的终端解释器,使用print打印当前的环境变量

1
2
3
print     #打印当前变量
serenv #设置环境变量
saveenv #保存环境变量到flash

这里uboot已经启动成功,下面我们使用uboot加载kernel,如果有板子的话一般都是通过下载到板子里面然后再启动,但是这样子比较浪费时间,这里我们采用tftp网口下载直接运行的方式运行kernel。

uboot引导kernel+rootfs

在uboot初始化完成之后会将需要传给内核的参数放到一段内存空间,然后运行内核 文件系统,内核会从这段空间取相应的信息MachineID bootargs console等等信息(个人理解,如果不正确Email联系)

tftp加载kernel

uboot通过以太网从ftp服务器获得kernel的uImage,所以我们在使用虚拟机Ubuntu系统要和qemu位于同一个网段,这里将Ubuntu设置位NAT模式。创建一个网桥,将ubuntu和qemu的网卡都连接到网桥。uImage和zImage的区别是uImage压缩过,解压之后uImage和zImage几乎相同。之前分析uboot的uboot代码的时候只支持uImage启动,但是可以通过修改uboot代码支持启动zImage,也不难。tftp加载kernel的原理就是在Ubuntu安装tftp服务器,uboot会从tftp下载内核。下面分步进行操作:

  1. 创建网桥和qemu的虚拟网卡tap0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    安装工具包:apt-get install uml-utilities bridge-utils

    修改/etc/network/interfaces文件:
    auto lo
    iface lo inet loopback
    auto tap0
    auto ens33
    auto tap0
    auto br0
    iface br0 inet dhcp
    bridge_ports all tap0
    重启,使用nmcli可以看到添加到的br0桥
    添加tap0网卡,并将网卡连接到br0:
    tunctl -t tap0 -u root
    brctl addif br0 tap0
    ifconfig tap0 0.0.0.0 promisc up
    配置之后重启网络: service networking restart
  2. 安装tftp服务器

    1
    2
    3
    4
    5
    6
     apt-get install tftp-hpa tftpd-hpa xinetd
    修改配置文件:/etc/default/tftpd-hpa :
    TFTP_USERNAME="tftp"
    TFTP_DIRECTORY="/home/linux/tftpboot" #这个是我们创建的tftp服务器存放文件路径,权限:0777
    TFTP_ADDRESS="0.0.0.0:69"
    TFTP_OPTIONS="-l -c -s"
  3. 编译内核:

    1
    2
    3
    4
    5
    6
    7
    8
    下载linux源码:https://mirrors.edge.kernel.org/pub/linux/kernel/
    进入源码根目录的Makefile:修改ARCH和CROSS_COMPILE
    ARCH ?=arm
    CROSS_COMPILE?=arm-none-linux-gnueabi-
    执行配置文件:make vexpress_defconfig (linux-5.2/arch/arm/configs 里面放的各种板子支持的配置文件)
    使用图形配置kernel支持nfs和tftp:
    make menuconfig
    安装生成: make LOADADDR=0x60003000 uImage -j6 # (-j6是多线程执行)
  4. qemu的启动脚本

    1
    2
    3
    4
    5
    6
    7
     qemu-system-arm \
    -M vexpress-a9 \
    -kernel /home/linux/armuboot/u-boot-2017.05/u-boot \
    -nographic \
    -m 512M \
    -net nic \
    -net tap,ifname=tap0
  1. 加载内核

    1. 手动加载

      1
      2
      3
      4
      5
      6
      在手动加载之前需要重启系统,并且确保tap0连接到br0网桥,使用nmcli命令查看br0的IP
      执行4步骤的脚本,在3秒内,任意键进入终端执行如下命令:
      setenv IPADDR 192.168.5.126
      setenv SERVERIP 192.168.5.128 #这里的IP是br0的IP
      tftp 0x60003000 uImage #将tftp的路径下的uImage下载到0x60003000地址
      bootm 0x60003000 #手动启动内核
    2. 自动加载

      1
      2
      3
      4
      5
      6
      7
      8
      进入uboot根目录 打开vim include/configs/vexpress_common.h
      修改如下:
      #define CONFIG_BOOTCOMMAND "tftp 0x60003000 uImage;tftp 0x60500000 vexpress-v2p-ca9.dtb; setenv bootargs 'root=/dev/mmcblk0 console=ttyAMA0';bootm 0x60003000 - 0x60500000;"

      #define CONFIG_IPADDR 192.168.5.126
      #define CONFIG_NETMASK 255.255.255.0
      #define CONFIG_SERVERIP 192.168.5.139
      执行4步骤的脚本,会相继看到下面两个过程

nfs加载rootfs

上面已经成功启动内核,但是还需要文件系统的支持才能进入命令行终端,可以使用busybox工具创建,可以参考网上教程。这里可以使用两种方式挂载rootfs,但是为了使用方便,我们通过nfs挂载,便于后续的开发调试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
安装:
sudo apt-get install nfs-kernel-server
配置:
在/etc/exports文件中添加:
/home/linux/armsystem/rootfs *(rw,sync,no_root_squash,no_subtree_check)
保存
重启服务:
service rpcbind restart
service nfs-kernel-server restart
进入uboot的根目录修改文件:
vim include/configs/vexpress_common.h
#define CONFIG_BOOTCOMMAND "tftp 0x60003000 uImage;tftp 0x60500000 vexpress -v2p-ca9.dtb; setenv bootargs 'root=/dev/nfs rw nfsroot=192.168.5.140:/home/ linux/armsystem/rootfs ip=192.168.5.126:192.168.5.140:255.255.255.0:eh0:off init=/linuxrc console=ttyAMA0';bootm 0x60003000 - 0x60500000;"
#define CONFIG_IPADDR 192.168.5.126
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_SERVERIP 192.168.5.140
重新编译uboot
执行上一节4步骤脚本

到此整个环境就搭建完成了。期间会遇见很多不可知的错误,慢慢研究就肯定能搞明白。

#nfs挂载一直失败

使用nfs挂在文件系统的时候,前面搭建一切顺利,但就是挂载不上去的原因就是较新版本的Ubuntu系统,只支持nfs协议3和协议4,但是uboot只支持nfs协议2,所以需要修改Ubuntu的配置文件如下:

1
2
3
vim  /etc/default/nfs-kernel-server
尾行添加:RPCNFSDOPTS="--nfs-version 2,3,4 --debug --syslog"
保存,重启nfs即可

总结

使用qemu模拟arm有利也有弊,我们不能真正的点亮LED灯,但是对于学习内核驱动,不用关注硬件,将会有更多的时间学习驱动。

文章作者: ZhaoH.T
文章链接: http://www.funful.ink/2019/07/30/2019-07-30-QEMU-ARM-A9-TFTP-NFS/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 FunfulBlog

评论