Vmware + Ubuntu14.04 + u-boot-tools
• 代码编辑管理工具:Git/vim
• 交叉编译器:gcc-arm-linux-gnueabi
Qemu 是纯软件实现的虚拟化模拟器,几乎可以模拟任何硬件设备,我们最熟悉的就是能够模拟一台能够独立运行操作系统的虚拟机,虚拟机认为自己和硬件打交道,但其实是和 Qemu 模拟出来的硬件打交道,Qemu 将这些指令转译给真正的硬件。
直接使用 apt 命令安装:
$ apt install qemu
使用命令 “qemu-system-
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 的内核可以通过 git clone 下载,也可以直接从 www.kernel.org 上下载。[ 版本号:linux-4.4.131 ]
ARCH=arm; CROSS_COMPILE=arm-linux-gnueabi-
CROSS_COMPILE 给出了编译程序所用的交叉工具链的名称。
ARCH=arm 表示 ARCH 给出了目标处理器的架构这里用的是 arm 处理器;
$ make vexpress_defconfig; make zImage
生成的 zImage 文件在 ./linux-xxx/arch/arm/boot 目录下。
$ make modules
dtb文件作用的描述是,使用dtb可以减少linux内核版本的数量。同一份 linux 内核代码可以在多个板卡上运行,每个板卡可以使用自己的dtb文件:
$ make dtbs
生成的 dtb 文件是 ./linux-xxx/arch/arm/boot/dts/vexpress-v2p-ca9.dtb
有了内核文件 zImage 和 dtb 文件之后,就可以尝试使用 qemu 运行内核了:
结果显示,当前内核中并没有根文件系统。
文件系统是对存储设备上的数据进行组织的机制;根文件系统是 Linux 内核启动后第一个挂载的文件系统,主要由基本的shell命令、各种库、字符设备、配置脚本组成。
busybox 是一个集成100多个 Linux 常用命令和工具的软件,它常用来制作嵌入式文件系统的软件工具。
下载网址: http://www.busybox.net/downloads/ [ 版本号:busybox-1.28.1 ]
ARCH=arm; CROSS_COMPILE=arm-linux-gnueabi-
$ make defconfig
$ make menuconfig
Settings -> Build options - 选中 Build static binary
$$ bin linuxrc sbin usr
$ 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个结点。
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-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-system-arm -M vexpress-a9 -m 512M -dtb ./vexpress-v2p-ca9.dtb -kernel ./zImage -append "root=/dev/mmcblk0 rw console=tty0" -sd rootfs.ext3
其中,/etc/init.d/rcS 是 Linux 的开机启动脚本。借助启动脚本可以设置各种程序开机后自动运行,也可以设置其他系统设置,这有点类似于 Windows 系统中的 Autobat 自动批处理文件。这里的根文件系统中并没有启动脚本。由于现在的虚拟机文件系统是可读可写的,可以直接在这里新建一个简单的脚本(也可以直接修改镜像文件 rootfs.ext3):
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)
下载地址: http://ftp.denx.de/pub/u-boot/ [ 版本号:u-boot-2017.07 ]
ARCH=arm; CROSS_COMPILE=arm-linux-gnueabi-
$ make vexpress_ca9x4_defconfig
$ make –j4
-j4,jobs=4,同时最多跑4个作业,make 自己会协调,如果 CPU 等资源不够可能小于4个。生成的 u-boot 和 u-boot.bin 文件在 u-boot-xxx 目录下。
$ qemu-system-arm -M vexpress-a9 -kernel u-boot -nographic -m 512M
在倒计时结束之前,按下任意键即可进入 u-boot 的命令行界面:
U-boot 发展到现在,他的命令行模式已经非常接近 Linux 下的 shell 了。在这里,你可以通过 "help xxx" 查看某个命令的具体用法。
为了使 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
跳过 u-boot 的命令行,让它自动加载 uImage 和 dtb 文件。接下来是一系列的初始化过程……,最后启动完成。
七、挂载 NFS 文件系统
前面的 u-boot 仍然是从 SD 卡加载镜像文件,这一节将从 NFS 中加载。
NFS(Network File System)即网络文件系统,是 FreeBSD 支持的文件系统中的一种,它允许网络中的计算机之间通过 TCP/IP 网络共享资源。在 NFS 的应用中,本地(qemu 的虚拟机) NFS 的客户端应用可以透明地读写位于远端(Ubuntu 上) NFS 服务器上的文件,就像访问本地文件一样。
显而易见的效果是,如果在 Ubuntu 上有个目录比如 rootfs/ 存放了 qemu 虚拟机的根文件系统,现在使用 NFS 将它挂载到虚拟机上,那么在虚拟机上操作它的文件系统和在 Ubuntu 上操作 rootfs 等效。反之亦然。这样可以大大节省存储空间和降低操作难度。
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 restart2) 内核配置
$ make menuconfig
让内核支持挂载 NFS 文件系统。File systems --> Network File Systems -->
$ 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;"
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"\
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:/#
在嵌入式开发环境搭建完成之后,就可以在开发板上开发运用程序和内核驱动程序。
感谢 CSDN 学院王老师的讲解:https://edu.csdn.net/course/detail/5437