qemu模拟arm嵌入式环境

一、安装qemu

1、下载编译安装

# wget https://download.qemu.org/qemu-4.2.0.tar.xz
# tar xvJf qemu-4.2.0.tar.xz
# cd qemu-4.2.0
# ./configure
# make
# make install

2、遇到的错误

ERROR: glib-2.22 gthread-2.0 is required to compile QEMU

一般使用

# sudo apt-get install libglib2.0-dev zlib1g-dev

就可以解决,但我的机器上不行,必须是libglib2.48版本以上的,所以只能网上下载编译安装了。

再安装一些必须库:

# sudo apt-get install libpixman-1-dev
# sudo apt-get install libsdl2*

3、检查是否安装正确

# qemu-system-arm -M ?

二、安装arm工具链

我们要使用arm工具链来编译uboot、内核、程序等,可以到arm官网下载。

当然也可以直接用命令下载:

# sudo apt install gcc-arm-linux-gnueabi

三、下载编译内核

1、下载
我选择的版本是4.0.2的

# wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.0.2.tar.xz

2、编译

# make vexpress_defconfig // 配置内核,这之后还可以make menuconfig微调
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  zImage -j4
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  modules 	-j4  // 编译驱动模块
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  dtbs -j4 // 编译设备树
前面三个可以直接使用:
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4

其中ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-可以去掉,但必须在Makefile修该:

arch ?= ARM, CROSS_COMPILE ?= arm-linux-gnueabi-

编译很长时间,成功后就可以看到需要的文件了:
1

四、制作根文件系统

构建嵌入式Linux文件系统有很多工具,如buildroot,busybox、OpenWRT等,我选用的是busybox。

1、下载

# wget https://busybox.net/downloads/busybox-1.32.0.tar.bz2

也可以直接去官网选择版本下载。

2、编译
基本跟编译内核差不多

# vim Makefile,   #ARCH ?= arm, CROSS_COMPILE ?= arm-linux-gnueabi-
# make defconfig
# make menuconfig #图形化配置,可以在settings->build options将其编译为static
# make -j4
# make install    #然后在当前目录下 _install 就有busybox生成的内容了(基本的命令和库)

3、制作根文件系统

# mkdir rootfs
# cp -ra _install/* rootfs/
# mkdir -p rootfs/lib
# cp -ra /usr/arm-linux-gnueabi/lib/* rootfs/lib/ (里面的 *.a 其实可以删掉 只用 .so)# cd rootfs
 #全是空文件夹
# mkdir dev proc sys tmp root var mnt# cd dev
# sudo mknod -m 666 tty1 c 4 1 #看起来是约定好的设备号,参看 ubuntu 主机中的
# 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#还有一个etc/

4、制作根文件系统磁盘映像

 # dd if=/dev/zero of=a9rootfs.ext3 bs=1M count=32
 # mkfs.ext3 a9rootfs.ext3
 # mount -t ext3 a9rootfs.ext3 tmpfs/ -o loop
 # cp -ra ../busybox-1.32.0/rootfs/* tmpfs/
 # umount tmpfs

根据自己的目录来。

所需的东西差不多了:
2

五、qemu 运行

1、直接启动kernel
 qemu-system-arm \
     -M vexpress-a9 \
     -m 512M \
     -kernel zImage \
     -dtb vexpress-v2p-ca9.dtb \
     -nographic \
     -append "root=/dev/mmcblk0 rw console=ttyAMA0" \
     -sd vexpress.img
 
 #-M 使用qemu仿真 vexpress-a9 machine
 #-m 指定qemu虚拟机内存 512M
 #-kernel 指定 qemu使用的kernel image,-dtb 指定 qemu boot kernel 时使用的设备树
 
 # -nographic 不使用图形化界面(串口输出)。启动lcd版本的qemu,除了需要去掉 -nographic 外, 
 # 还需要将 console=ttyAMA0 改为console=tty0,因为标准终端已经重定向到lcd了
 #(设备则由/dev/ttyAMA0变为/dev/tty0了)
 
 # -append "xx"  指定kernel启动参数,root=/dev/mmcblk0 告诉kernel,rootfs文件系统映像
 # 在 /dev/mmcblk0,以rw方式挂载。这个选项配合-sd工作,sd/emmc设备都是mmc接口,mmc接口的
 # 第一个设备就是mmcblk0。-append还可以更完善,比如 -append "init=/linuxrc root=/dev/mmcblk0 
 # rw rootwait earlyprintk console=ttyAMA0",init=/linuxrc 告诉kernel起来后执行一下 /linuxrc。
 # 启动后报告 can't run '/etc/init.d/rcS': No such file or directory,这是linux启动后执行
 # 到的脚本,我们可以创建这个文件(并chmod 777),随便echo点东西即可。(etc作为kernel启动后的配置指示,
 # 可以完善的更好, etc.tar.gz)。
 
 # -sd 指示qemu的硬件连接状态,连接了一个sd卡(mmc接口),sd卡中的映像内容是 vexpress.img。没有文件系统
 # kernel会打印 Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0),
 # 表示没有文件系统可以挂载。配合-append里的root=xx使用。
 
 #退出qemu就 ps -a 并 kill xx(或者  ps -A | grep qemu-system-arm | awk '{print $1}' | xargs sudo kill)
2、通过uboot启动内核

uboot 加载 kernel 可以从sd卡拿,但是sd卡已经存放了rootfs,没法和kernel image放一起,所以只能通过 tftp 从 ubuntu主机拿到。下面就是使用的这个办法。
当然也可以制作sd卡映像时制作两个分区,一个放kernel+dtb(分区格式只要uboot认识即可),另一个放rootfs(必须是linux指定的ext格式)。

2.1 配置QEMU Tap网络

既然要使用tftp,那么必须要配置QEMU 网络。qemu可以使用Tap / Tunnel模式将私有网络与ubuntu结合使用。

  • 主机安装工具包:uml-utilities bridge-utils
# apt-get install uml-utilities bridge-utils
  • 创建 tun 设备文件:/dev/net/tun(Ubuntu14.04 中默认支持)
  • 修改 /etc/network/interfaces 文件, 添加:(eth0 为当前宿主机的网络接口,可以使用 “ifconfig” 查看,视情况修改)
auto eth0
auto br0
iface br0 inet dhcp
bridge_ports eth0

也可以部不修改/etc/network/interfaces,直接通过命令来实现,在主机的网卡上建立一个虚拟网桥,然后再虚拟网桥上创建虚拟的网卡,把创建好的虚拟网卡分配给qemu客户机使用

# brctl addbr br0    # 添加bridge br0
# brctl addif br0 eth0 # 将br0 与eth0 绑定
# brctl stp br0 on    # 将br0设置为STP协议
# ifconfig eth0 0     # 将eth0 的IP设置为0,因为eth0已经工作在链路层,不需要IP
# dhclient br0       # 设置br0参数
# route            # 查看路由表
  • 配置 /etc/qemu-ifup、/etc/qemu-ifdown 脚本

qemu-ifup

#!/bin/sh
 
echo sudo tunctl -u $(id -un) -t $1
sudo tunctl -u $(id -un) -t $1
 
echo sudo ifconfig $1 0.0.0.0 promisc up
sudo ifconfig $1 0.0.0.0 promisc up
 
echo sudo brctl addif br0 $1
sudo brctl addif br0 $1
 
echo brctl show
brctl show
 
# sudo ifconfig br0 192.168.232.20

qemu-ifdown

#!/bin/sh
 
echo sudo brctl delif br0 $1
sudo brctl delif br0 $1
 
echo sudo tunctl -d $1
sudo tunctl -d $1
 
echo brctl show
brctl show
  • 修改 /etc/network/interfaces 文件 需重启生效
2.2 安装配置tftp

安装tftp工具:

apt-get install tftp-hpa tftpd-hpa xinetd

修改配置文件:/etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/qigaohua/work/qrs/qemuarm/"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-l -c -s"

重启 tftp 服务:

/etc/init.d/tftpd-hpa restart
2.3 编译uImage

uboot 启动需要uImage ,前面使用的是zImage

 make LOADADDR=0x60003000 uImage -j4
2.4 编写启动脚本boot.sh
#!/bin/bash

# https://www.qemu.org/2018/05/31/nic-parameter/ 不能识别vlan选项
# -net nic,vlan=0 -net tap,vlan=0,ifname=tap0
# -netdev tap,id=n1 -device e1000,netdev=n1
qemu-system-arm  \
    -M vexpress-a9 \
    -m 512M \
    -kernel u-boot \
    -dtb vexpress-v2p-ca9.dtb \
    -net nic -net tap,ifname=tap \
    -nographic \
    -append "root=/dev/mmcblk0 rw console=ttyAMA0"\
    -sd a9rootfs.ext3

运行看看:
在uboot命令行输入以下:

 setenv ipaddr 192.168.223.108  // 根据自己网络情况而定
 setenv gatewayip 192.168.223.1
 setenv netmask 255.255.255.0
 setenv serverip 192.168.223.109
 setenv bootargs 'root=/dev/mmcblk0 rw console=ttyAMA0' //如果是图形化启动,console=tty0;
 saveenv

3
然后再输入:

# tftp 0x60003000 uImage // 将镜像拷贝至 0x60003000,类似于 u-boot 的重定向过程
# tftp 0x60500000 vexpress-v2p-ca9.dtb // 将 dtb 文件拷贝至 0x60500000 位置。从这个头文件中可以知道,RAM 的地址是从 0x60000000 ~ 0x7fffffff
# bootm 0x60003000 - 0x60500000

4
5
5
OK.

上面需要手动在uboot命令行输入,可以直接在 uboot目录下的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; "
 
/* 配置开发板、主机IP地址 */
#define CONFIG_IPADDR 192.168.223.108
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_SERVERIP 192.168.223.109

再重新编译uboot,应该就不需要手动输入了,这个没有测试。

3. 挂载 NFS 文件系统

前面的 u-boot 仍然是从 SD 卡加载镜像文件,这一节将从 NFS 中加载。

NFS(Network File System)即网络文件系统,它允许网络中的计算机之间通过 TCP/IP 网络共享资源。在 NFS 的应用中,本地(qemu 的虚拟机) NFS 的客户端应用可以透明地读写位于远端(Ubuntu 上) NFS 服务器上的文件,就像访问本地文件一样。
如果在 Ubuntu 上有个目录比如 rootfs/ 存放了 qemu 虚拟机的根文件系统,现在使用 NFS 将它挂载到虚拟机上,那么在虚拟机上操作它的文件系统和在 Ubuntu 上操作 rootfs 等效。反之亦然。这样可以大大节省存储空间和降低操作难度。

在宿主机安装nfs:

# apt install nfs-kernel-server

修改nfs配置文件/etc/exports

/home/qigaohua/work/qrs/qemuarm/rootfs     *(rw,sync,no_root_squash,no_subtree_check)

开启 NFS 服务

# /etc/init.d/nfs-kernel-server restart

注意: 编译内核需让内核支持挂载 NFS 文件系统(默认支持的),可使用make menuconfig ⇒ File systems --> Network File Systems --> 查看

运行qemu, 把上面boot.sh最后一行-sd去掉,启动后在uboot命令行输入一下命令:

# setenv ipaddr 192.168.223.108
# setenv gatewayip 192.168.223.1
# setenv netmask 255.255.255.0
# setenv serverip 192.168.223.109
# setenv bootargs 'root=/dev/nfs rw nfsroot=192.168.223.109:/home/qigaohua/work/qrs/qemuarm/rootfs init=/linuxrc ip=192.168.223.108 console=ttyAMA0'  // 使用nfs文件系统
# saveenv
# tftp 0x60003000 uImage
# tftp 0x60500000 vexpress-v2p-ca9.dtb
# bootm 0x60003000 - 0x60500000

运行成功后,在本地nfs目录添加文件后,qemu arm模拟机也同样添加了。


六、qemu 模拟机连接外网

当上面启动成功后,我们用ifconfig看看接口情况:
7
我发现ping baidu.com 不同,不能访问外网。下面解决该问题。

先看下路由情况:route -n
若出现 route: can’t open ‘/proc/net/route’: No such file or directory

mkdir proc // 如不存在该目录
mount -t proc proc proc/
8
如果Destination 有default 项(即 0.0.0.0),无需处理,否则需要添加路由,上图可知,我的并没有。添加路由:

# route add default gw 192.168.223.1 dev eth0

ping 下百度,发现可以了
9
如果你还不能ping baidu.com, 可能是域名解析问题,创建/etc/resolv.conf,添加:
10
根据自己的情况而定。


七、其他

上面说了制作sd卡映像时制作两个分区,一个放kernel+dtb,另一个放rootfs,不需要通过tftp加载内核启动。

1、制作多分区镜像
 #制作一个含有两个分区的镜像,第一个分区放kernel+dtb,第二个分区是rootfs用于挂载
 dd if=/dev/zero of=kernel-rootfs.img bs=1M count=64
 sudo parted kernel-rootfs.img --script -- mklabel msdos   #这三步用fdisk做也行
 sudo parted kernel-rootfs.img --script -- mkpart primary fat32 2048s 40960s
 sudo parted kernel-rootfs.img --script -- mkpart primary ext4 40961s -1
 #fdisk kernel-rootfs.img                                  #分两个区,2048-40960,40961-#建立映射,然后格式化两个分区
 sudo losetup -f --show kernel-rootfs.img
 sudo kpartx -va /dev/loop0
 sudo mkfs.vfat /dev/mapper/loop0p1
 sudo mkfs.ext4 /dev/mapper/loop0p2
 ​
 #mount到ubuntu主机
 mkdir kernel rootfs
 sudo mount /dev/mapper/loop0p1 kernel
 sudo mount /dev/mapper/loop0p2 tmpfs

# 拷贝uImage、vexpress-v2p-ca9.dtb到kernel目录,拷贝rootfs/*到tmpfs目录

 sudo umount kernel
 sudo umount tmpfs
 ​
 sudo kpartx -d /dev/loop0
 sudo losetup -d /dev/loop0
 ​
 rm -rf kernel tmpfs
2、运行qemu
qemu-system-arm -M vexpress-a9 -m 512M -kernel u-boot -nographic -append "root=/dev/mmcblk0 console=ttyAMA0" -sd kernel-rootfs.img

uboot起来后,在uboo命令行中输入:

 fatls mmc 0:1
 fatload mmc 0:1 60003000 uImage
 fatload mmc 0:1 60500000 vexpress-v2p-ca9.dtb
 setenv bootargs 'init=/linuxrc root=/dev/mmcblk0p2 rw rootwait earlyprintk console=ttyAMA0'
 bootm 60003000 - 60500000

root=/dev/mmcblk0p2,告诉kernel根文件系统在第0个mmc接口设备的第2个分区。(rootwait 表示等待 mmc 设备初始化完成以后再挂载, earlyprintk 打开早期打印)

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值