qemu实践一:arm + linux + u-boot + NFS


一、基本环境

Vmware + Ubuntu14.04 + u-boot-tools

• 代码编辑管理工具:Git/vim
• 交叉编译器:gcc-arm-linux-gnueabi

二、qemu 安装

Qemu 是纯软件实现的虚拟化模拟器,几乎可以模拟任何硬件设备,我们最熟悉的就是能够模拟一台能够独立运行操作系统的虚拟机,虚拟机认为自己和硬件打交道,但其实是和 Qemu 模拟出来的硬件打交道,Qemu 将这些指令转译给真正的硬件。

1、自动安装

直接使用 apt 命令安装:
$    apt install qemu
使用命令 “qemu-system-”可以查看 qemu 支持的处理器体系。

2、手动安装

1)  首先安装 qemu 依赖包:zlib1g-dev、libglib2.0-0 libglib2.0-dev、libsdl1.2-dev、libpixman-1-dev libfdt-dev;
其它的功能包如:python, pkg-config, bison, flex……

2)  下载 qemu 源码:

$    git clone git://git.qemu-project.org/qemu.git

3)  切换到一个稳定版本:

$    git checkout v2.8.0

4)  编译配置:

$    ./configure --target-list=arm-softmmu --audio-drv-list=
如此配置的 qemu 模拟器只有 qemu-system-arm

5)  编译安装:

$    make ; make install

三、开发板简介

qemu 支持的开发板非常多:
    • cubieboard
    • i.mx25
    • mainstone
    • Samsung Exynos4210
    • Vexpress-a9
    • Vexpress-a15

    • …

本次需要仿真的开发板为 Vexpress-a9 。Vexpress 的全称为 versatile express family ,它是ARM公司自己推出的开发板,主要用于SOC厂商设计、验证和测试自己的SOC芯片。

四、编译 Linux 内核和 dtb 文件

1、下载 Linux 内核

Linux 的内核可以通过 git clone 下载,也可以直接从  www.kernel.org 上下载。[ 版本号:linux-4.4.131 ]

2、修改 Makefile

ARCH=arm; CROSS_COMPILE=arm-linux-gnueabi-
CROSS_COMPILE 给出了编译程序所用的交叉工具链的名称。
ARCH=arm 表示 ARCH 给出了目标处理器的架构这里用的是 arm 处理器;

3、编译内核

$    make vexpress_defconfig; make zImage
生成的 zImage 文件在 ./linux-xxx/arch/arm/boot 目录下。

4、编译模块

$    make modules

5、编译 dtb 文件

dtb文件作用的描述是,使用dtb可以减少linux内核版本的数量。同一份 linux 内核代码可以在多个板卡上运行,每个板卡可以使用自己的dtb文件:

$    make dtbs

生成的 dtb 文件是  ./linux-xxx/arch/arm/boot/dts/vexpress-v2p-ca9.dtb

6、启动内核

有了内核文件 zImage 和 dtb 文件之后,就可以尝试使用 qemu 运行内核了:

$    qemu-system-arm -M vexpress-a9 -m 512M -kernel zImage -dtb vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
其中,-M 指定开发板; -m 指定内存大小; -kernel 指定内核镜像; -dtb 指定 dtb 文件; -nographic 表示不采用图形化界面启动 qemu; -append 添加其它参数: console 指定为当前终端。运行效果如下:
qemu实践一:arm + linux + u-boot + NFS_第1张图片

结果显示,当前内核中并没有根文件系统。

五、使用 busybox 制作根文件系统

文件系统是对存储设备上的数据进行组织的机制;根文件系统是 Linux 内核启动后第一个挂载的文件系统,主要由基本的shell命令、各种库、字符设备、配置脚本组成。

busybox 是一个集成100多个 Linux 常用命令和工具的软件,它常用来制作嵌入式文件系统的软件工具。

1、下载 busybox 源码

下载网址: http://www.busybox.net/downloads/ [ 版本号:busybox-1.28.1 ]

2、修改 Makefile

ARCH=arm; CROSS_COMPILE=arm-linux-gnueabi-

3、编译配置

$    make defconfig

$    make menuconfig
Settings -> Build options - 选中 Build static binary
qemu实践一:arm + linux + u-boot + NFS_第2张图片

4、编译和安装
$    make; make install
生成的根文件系统目录: /busybox-1.28.1/_install
$    ls /busybox-1.28.1/_install

$$    bin  linuxrc  sbin  usr

5、制作根文件系统

$     mkdir rootfs
$     mkdir rootfs/lib
$     cp –r _install/* ../rootfs
$     cp -p /usr/arm-linux-gnueabi/lib/* rootfs/lib
$     mkdir -p rootfs/dev/; cd rootfs/dev
$     mknod –m 666 tty1 c 4 1
$     mknod –m 666 tty2 c 4 2
$     mknod –m 666 tty3 c 4 3
$     mknod –m 666 tty4 c 4 4
$     mknod –m 666 console c 5 1
$     mknod –m 666 null c 1 3

文件夹 rootfs 保存了根文件系统。其中,/bin、/sbin、和 /usr 来自 busybox-xxx/_install;/lib 来自 /usr/arm-linux-gnueabi/lib/;另外在 /dev 下新建了如上6个结点。

6、制作 SD 卡文件系统镜像

1)  生成镜像文件:
$    dd if=/dev/zero of=rootfs.ext3 bs=1M count=32

2)  格式化镜像文件为 ext3 文件系统:
$    mkfs.ext3 rootfs.ext3
EXT3是第三代扩展文件系统(英语:Third extended filesystem,缩写为ext3),是一个日志文件系统,常用于Linux操作系统。磁盘文件系统,在首次使用前,需要格式化。

3)  将 rootfs 下的各个文件拷贝到文件系统镜像中

访问或者修改存储设备中的文件之前,必须将文件所在的分区挂载到一个已存在的目录上,然后通过访问这个目录来访问存储设备。

  a、挂载镜像:
  $    mount -t ext3 rootfs.ext3 /mnt/ -o loop

  b、拷贝文件
  $    cp -r rootfs/* /mnt

  c、卸载镜像

  $    umount /mnt

这样,一个简单的镜像文件 rootfs.ext3 就完成了。
qemu实践一:arm + linux + u-boot + NFS_第3张图片

7、启动内核
1)  启动内核,挂载rootfs

 $     qemu-system-arm -M vexpress-a9 -m 512M -dtb ./vexpress-v2p-ca9.dtb -kernel ./zImage -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd rootfs.ext3
SD 卡在板子上是直接插在 mmc/sd/sdio 插槽上的,走的是 mmc总 线,所以是mmcblk0; rw 表示可读可写;将 ttyAMA0 指定为默认的 console 。
qemu实践一:arm + linux + u-boot + NFS_第4张图片

2)  图形化启动内核

 $    qemu-system-arm -M vexpress-a9 -m 512M -dtb ./vexpress-v2p-ca9.dtb -kernel ./zImage -append "root=/dev/mmcblk0 rw console=tty0" -sd rootfs.ext3

qemu实践一:arm + linux + u-boot + NFS_第5张图片

其中,/etc/init.d/rcS 是 Linux 的开机启动脚本。借助启动脚本可以设置各种程序开机后自动运行,也可以设置其他系统设置,这有点类似于 Windows 系统中的 Autobat 自动批处理文件。这里的根文件系统中并没有启动脚本。由于现在的虚拟机文件系统是可读可写的,可以直接在这里新建一个简单的脚本(也可以直接修改镜像文件 rootfs.ext3):
qemu实践一:arm + linux + u-boot + NFS_第6张图片

六、使用 u-boot 加载 Linux 内核

u-boot 和 Linux 内核本质上都是裸机程序,区别是内核运行起来后可以分为应用层和内核层,分层后,两层的权限不同,内存访问和设备操作的管理上更加精细;而 u-boot 可以被配置也可以做移植,其主要作用是用来启动 linux 内核,因为 CPU 不能直接从块设备中执行代码,需要把块设备中的程序复制到内存中,而复制之前还需要进行很多初始化工作,如时钟、串口、dram等;

区分一、uboot / bootLoader / BIOS

(1) PC 机中的引导加载程序由 BIOS(其本质是一段固件程序)和 GRUB 或 LILO 一起组成。BIOS 在完成硬件检测和资源分配后,将硬盘中的引导程序读到系统内存中然后将控制权交给引导程序。引导程序的主要任务是将内核从硬盘上读到内存中,然后跳转到内核的入口点去运行,即启动操作系统。
(2) BootLoader 是一段小程序,可以把它想象成 PC 机 linux 上的 GRUB/LILO 引导程序,只不过在嵌入式 linux 中,没有 BIOS,而是直接从 flash 中运行,来装载内核。它可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。

(3) 而 uboot 是 BootLoader 的一种,一般应用于 Linux;类似的还有 eboot(wince)。

区分二、vmlinuz / zImage / uImage

(1) uboot 编译后生成了一个 ELF 格式的可执行程序 u-boot,对应的烧录程序是 u-boot.bin,它是由 u-boot 使用 arm-linux-objcopy 得到(主要目的是去掉一些无用的);
(2) Linux kernel 经过编译后也会生成一个 ELF 格式的可执行程序,叫 vmlinuz 或者 vmlinux,这个就是原始的未经任何处理加工的原版内核 ELF 文件,嵌入式部署烧录的一般不是这个 vmlinuz,而是使用 objcpy 工具去制作成烧录镜像格式的 Image,制作镜像主要目的是缩减大小,节俭磁盘;
(3) 原则上 Image 就可以直接被烧录到 Flash,但实际上并不是这么简单,实际上 Linux 的作者们觉得 Image 太大了,对其进一步压缩,并且在压缩后的前一段部分,附加了一部分解压缩代码,构成了一个压缩可是的镜像就是 zImage(因为当年 Image 大小刚刚比一张软盘大,(软盘有两种,1.2M 和 1.4M,Image 比 1.4M 大一点),为了节省一张软盘的钱,于是乎,设计了这种压缩 Image 成 zImage 的技术);
(4) uboot 为了启动 Linux 内核,还发明了一种内核格式叫 uImage。uImage 是由 zImage 加工得到的,uboot 中有一个工具,可以将 zImage 加工生产 uImage。注意:uImage 不管 Linux 内核的事,Linux 内核只管生成 zImage 即可,然后 uboot 中的 mkimage 工具再去由 zImage 加工生成 uImage 来给 uboot 启动。这个加工的过程是在 zImage 前面加上 64 字节的 uImage 的头信息即可;

(5) 原则上 uboot 启动时应该给它 uImage 格式的内核镜像,但是实际上uboot也可以支持 zImage,是否支持就看 x210_sd.h 中是否定义了 LINUX_ZIMAG_MAGIC 这个宏。所以有些 uboot 支持 zImage 启动,有些则不支持。但是所有的 uboot肯定都支持 uImage 启动。

uboot要启动内核,分为2个步骤:第一步是将内核镜像从启动介质中加载到DDR中,第二步是去DDR中启动内核镜像。

这里的启动介质包括: NOR/NAND flash、前文说的 SD 卡、以及从网络加载(如七中的 NFS

1、下载源码

下载地址: http://ftp.denx.de/pub/u-boot/  [ 版本号:u-boot-2017.07 ]

2、修改 Makefile

ARCH=arm;  CROSS_COMPILE=arm-linux-gnueabi-

3、编译配置

$     make vexpress_ca9x4_defconfig

4、编译

$     make –j4

-j4,jobs=4,同时最多跑4个作业,make 自己会协调,如果 CPU 等资源不够可能小于4个。生成的 u-boot 和 u-boot.bin 文件在 u-boot-xxx 目录下。

5、运行 u-boot

$    qemu-system-arm -M vexpress-a9 -kernel u-boot -nographic -m 512M

在倒计时结束之前,按下任意键即可进入 u-boot 的命令行界面:

qemu实践一:arm + linux + u-boot + NFS_第7张图片

U-boot 发展到现在,他的命令行模式已经非常接近 Linux 下的 shell 了。在这里,你可以通过 "help xxx" 查看某个命令的具体用法。

6、qemu 网络功能设置
1)  配置环境

为了使 qemu 的虚拟机能够与外界(Ubuntu)通信,通常需要添加如下的参数:

-net nic,vlan=0 -net tap,vlan=0,ifname=tap0 

其中,
-net nic,为虚拟机创建虚拟机网卡。Qemu 支持的常用网卡包括 NE2000、rtl8139、pcnet32 等。可以在命令行选项中加入 "model=pcnet" ,从而添加一块 pcnet 型的以太网卡,如果省略 model 参数,则会默认选择一种网卡(虚拟机启动后执行 "lspci" 命令可以查看);
vlan=0,表示默认的网卡连入 vlan0。qemu 可以为虚拟机添加多块网卡,属于同一个网络的两块网卡可以直接通信;
各个 vlan 通过 qemu 可以有 4 种通信方式与外界联网,分别是 User mode stack、socket、TAP 和 VDE。
-net tap,表示通过 TAP 设备通信。这种方式首先需要在宿主机中创建并配置一个 TAP 设备,qemu 进程将该 TAP 设备连接到虚拟机 VLAN 中。其次,为了实现虚拟机与外部网络的通信,在宿主机中通常还要创建并配置一个网桥,并将宿主机的网络接口(通常是eth0)作为该网桥的一个接口。最后,只要将 TAP 设备作为网桥的另一个接口,虚拟机 VLAN 通过 TAP 设备就可以与外部网络完全通信了。这是因为,宿主机的 eth0 接口作为网桥的接口,与外部网络连接;TAP 设备作为网桥的另一个接口,与虚拟机 VLAN 连接,这样两个网络就连通了。此时,网桥在这两个网络之间转发数据帧;

ifname=tap0,网卡逻辑名为 tap0。

为了实现以上功能,在以下三个方面都要做出一定的调整。

1)  内核配置

• 需要将内核编译为 uImage 格式
• 需要指定 uImage 的加载地址
• 编译时指定:
$     make LOADADDR=0x60003000 uImage -j4

2)  主机端配置

a、tap 支持

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

auto lo
iface lo inet loopback

auto eth0
auto br0
iface br0 inet dhcp
	bridge_ports eth0
• 配置 /etc/qemu-ifup、/etc/qemu-ifdown 脚本

--qemu-ifup--(192.168.232.20 这里选择和 eth0 同一网段的地址)

#!/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

重启生效

b、tftp 支持

•  安装tftp工具:

$     apt-get install tftp-hpa tftpd-hpa xinetd

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

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/qemu-test/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-l -c -s"

•  创建 tftp 目录:

$     mkdir /home/qemu-test/tftpboot; chmod 777 tftpboot

•  重启 tftp 服务:

$     /etc/init.d/tftpd-hpa restart

3)  u-boot 自动化引导

•  修改 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.232.1
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_SERVERIP 192.168.232.20

修改此头文件是为了使 u-boot 命令行操作更便捷,真实开发过程中不建议这么做。其中,
tftp 0x60003000 uImage,将镜像拷贝至 0x60003000,类似于 u-boot 的重定向过程;
tftp 0x60500000 vexpress-v2p-ca9.dtb,将 dtb 文件拷贝至 0x60500000 位置。从这个头文件中可以知道,RAM 的地址是从 0x60000000 ~ 0x7fffffff;
setenv bootargs 'root=/dev/mmcblk0 console=ttyAMA0',如果是图形化启动,console=tty0;
bootm 0x60003000 - 0x60500000,u-boot 从 0x60003000 这个地址启动镜像,启动时指定了 dtb 的位置。

•  重新编译 u-boot

$     make –j4

2)  运行 u-boot 并让它自动加载镜像和 dtb 文件

qemu实践一:arm + linux + u-boot + NFS_第8张图片

qemu实践一:arm + linux + u-boot + NFS_第9张图片

跳过 u-boot 的命令行,让它自动加载 uImage 和 dtb 文件。接下来是一系列的初始化过程……,最后启动完成。

qemu实践一:arm + linux + u-boot + NFS_第10张图片

七、挂载 NFS 文件系统

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

NFS(Network File System)即网络文件系统,是 FreeBSD 支持的文件系统中的一种,它允许网络中的计算机之间通过 TCP/IP 网络共享资源。在 NFS 的应用中,本地(qemu 的虚拟机) NFS 的客户端应用可以透明地读写位于远端(Ubuntu 上) NFS 服务器上的文件,就像访问本地文件一样。

显而易见的效果是,如果在 Ubuntu 上有个目录比如 rootfs/ 存放了 qemu 虚拟机的根文件系统,现在使用 NFS 将它挂载到虚拟机上,那么在虚拟机上操作它的文件系统和在 Ubuntu 上操作 rootfs 等效。反之亦然。这样可以大大节省存储空间和降低操作难度。

1、配置环境

1)  主机端配置

•  安装应用

$ apt install nfs-kernel-server

•  配置 NFS。在 /etc/exports 文件中添加:

/home/qemu-test/rootfs *(rw,sync,no_root_squash,no_subtree_check)

• 开启 NFS 服务

$ /etc/init.d/rpcbind restart
$ /etc/init.d/nfs-kernel-server restart

2)  内核配置

$    make menuconfig

让内核支持挂载 NFS 文件系统。File systems --> Network File Systems -->

qemu实践一:arm + linux + u-boot + NFS_第11张图片

$    make -j4

3)  u-boot 配置

•  修改 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.232.20:/home/qemu-test/rootfs init=/linuxrc \
	ip=192.168.232.21\
	console=ttyAMA0'; \
	bootm 0x60003000 - 0x60500000;"	
2、启动 u-boot 并在内核中挂载 NFS 文件系统
qemu-system-arm \
	-M vexpress-a9 \
	-kernel u-boot \
	-m 512M \
	-nographic \
	-net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \
	-append "root=/dev/mmcblk0 rw console=ttyAMA0"\

八、完善根文件系统

1、新建 etc 目录
qemu实践一:arm + linux + u-boot + NFS_第12张图片

2、新建 tmp、sys、var、proc 目录

3、启动 Linux,挂载 NFS 文件系统

•  Linux 内核启动之后,挂载 NFS 根文件系统
•  开启 Linux 的第一个用户进程:init
•  init 进程 bootargs -> init=… -> 执行 inittab 脚本
•  inittab 脚本首先会执行 init.d/rcS 脚本
•  rcS 脚本:执行 mount –a 读取 fstab 挂载各种文件系统
•  inittab:接着会启动 console
•  启动 shell:/bin/sh
•  在启动 /bin/sh 之前先执行 profile 文件
    可以在 profile 中指定 shell 的显示格式,比如 root@ubuntu:/# 
qemu实践一:arm + linux + u-boot + NFS_第13张图片

九、在虚拟板上运行应用和内核驱动

在嵌入式开发环境搭建完成之后,就可以在开发板上开发运用程序和内核驱动程序。


感谢 CSDN 学院王老师的讲解:https://edu.csdn.net/course/detail/5437


你可能感兴趣的:(虚拟机)