Qemu虚拟Arm开发板
实验说明
实验主机系统为debian9。ubuntu系统安装方法相似。可以通过qemu启动bootloader,再由bootloader启动内核,也可以直接由qemu启动内核。
安装qemu虚拟机
1 | apt install qemu |
交叉编译器安装
1 | apt install gcc-arm-linux-gnueabi |
安装u-boot-tools
该工具主要为了给内核编译以及文件系统制作提供mkimage程序
1 | apt-get install u-boot-tools |
uboot编译(可选)
1.获取uboot源码,本实验以u-boot-2016.09.tar.bz2为例
1 | wget ftp://ftp.denx.de/pub/u-boot/u-boot-2016.09.tar.bz2 |
2.根据qemu支持的机器配置uboot
获取qemu支持的机器列表:qemu-system-arm -machine help
1 | root@debian:~/work/arm/env# qemu-system-arm -machine help |
这里使用arm官方出的开发板vexpress-a9,如下配置uboot
1 | make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_ca9x4_defconfig |
3.编译uboot
1 | make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- |
编译后得到的目标程序为u-boot
linux内核编译
1.获取内核,这里以linux-5.3.4.tar.xz为例
1 | wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.3.4.tar.xz |
2.配置平台和交叉编译器
为了方便修改和编译,直接修改Makefile以配置平台和编译工具链。
1 | ARCH=arm |
3.配置board
根据编译uboot阶段选用的开发板,配置内核,这里是vexpress_defconfig
1 | make vexpress_defconfig |
4.编译内核
在这里指定了内核的加载地址为0X60003000,在uboot引导内核的时候也需要设置该地址。
1 | make LOADADDR=0X60003000 uImage -j2 |
内核目标文件为arch/arm/boot/uImage
5.编译设备树
1 | make vexpress-v2p-ca9.dtb |
设备树目标文件为arch/arm/boot/dts/vexpress-v2p-ca9.dtb
文件系统制作(busybox)
1.下载最新busybox
1 | wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2 |
2.配置busybox交叉编译工具前缀:make menuconfig
1 | cross compile prefix 配置为arm-linux-gnueabi- |
3.编译和安装:make && make install
安装完成后在其顶层目录生成_install目录。将其拷贝为rootfs目录。
1 | cp _install ../rootfs |
4.在rootfs中创建其它需要的目录
1 | mkdir mkdir mnt tmp var sys proc etc lib dev sbin root home |
5.在dev目录下创建console和null文件
1 | mknod -m 666 dev/console c 5 1 |
6.添加配置文件以及修改配置文件
将busybox的examples/bootfloppy/etc中的所有文件拷贝到rootfs/etc中。
1 | cp busybox-1.31.1/examples/bootfloppy/etc/* rootfs/etc -arf |
修改rootfs/etc/fstab内容为:
1 | proc /proc proc defaults 0 0 |
修改rootfs/etc/profile内容为:
1 | PS1='[\u@\h \W]\# ' |
修改rootfs/etc/inittab内容为:
1 | ::sysinit:/etc/init.d/rcS |
修改rootfs/etc/init.d/rcS内容为:
1 | /bin/mount -n -t ramfs ramfs /var |
创建rootfs/etc/hostname文件
1 | touch rootfs/etc/hostname |
创建rootfs/passwd文件
1 | touch rootfs/etc/passwd |
7.将所使用的编译器自带的动态库拷贝到rootfs/lib目录中。
1 | cp /usr/arm-linux-gnueabi/lib/* rootfs/lib/ -arf |
如果在busybox配置中选择了Settings->build Options ->Build busybox as a static binary,那么此处不需要拷贝动态库文件,但是要添加自己的程序到新做的文件系统中,则需要静态编译或者同事添加动态库。
文件系统目标目录文件为rootfs
制作ramdisk文件系统镜像(可选)
系统可以从内存中加载,也可以从其它介质中加载,如emmc,nandflash等,如果不需要从内存加载系统,则不需要制作ramdisk系统镜像。
1.安装genext2fs
1 | apt install genext2fs |
2.将rootfs制作成ext2镜像格式
1 | genext2fs -b 32768 -d rootfs ramdisk |
镜像大小为32768kb,即32M,生成的镜像名为ramdisk
3.压缩ramdisk镜像为ramdisk.gz
1 | gzip -v9 -f ramdisk |
4.制作uboot格式的ramdisk镜像
1 | mkimage -n 'uboot ext2 ramdisk rootfs' -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz ramdisk.img |
最终生成ramdisk.img
5.内核配置支持
要支持ramdisk系统,linux内核需要以下配置项的支持
General setup->Initial RAM filesystem and RAM disk(initramfs/initrd) support
Device Drivers->Block device->RAM block device support以及Default RAM disk size kbytes的值修改为di2步中ramdisk的大小,即32768
- File systems->Second extended fs support
系统加载和启动
系统的存储介质可以是emmc,nandflash,sd卡,网络等。
通过SD卡启动ramdisk系统(可选)
1.制作一个虚拟SD卡,大小为512M,文件系统格式为ext2
1 | dd if=/dev/zero of=virtsd.ext2 bs=1M count=512 |
制作好的目标sd卡是virtsd.ext2
2.将目标程序拷贝到sd卡
将内核,设备树以及ramdisk文件系统拷贝到sd卡中
1 | mount -t ext2 virtsd.ext2 /mnt -o loop |
3.通过qume虚拟机启动uboot
启动qemu虚拟机,设定机器为vexpress-a9 加载程序为u-boot sd卡为virtsd.ext2 内存为256M,不需要图形界面
1 | qemu-system-arm -M vexpress-a9 -kernel u-boot -sd virtsd.ext2 -nographic -m 256M |
待uboot启动进入倒计时的时候按任意键进入uboot操作界面,然后设置uboot启动参数
4.启动内核
在uboot操作界面中设置启动参数设置如下:
1 | 暂时不需要设置 |
将内核,设备树以及ramdisk文件系统从sd卡读到内存中:
1 | ext2load mmc 0 60003000 uImage;ext2load mmc 0 61000000 vexpress-v2p-ca9.dtb;ext2load mmc 0 62000000 ramdisk.img |
启动系统
1 | bootm 60003000 62000000 61000000 |
如果一切正常的话,就会成功进入到busybox系统。
直接从SD卡启动系统内核(可选)
1.制作一个虚拟SD卡,大小为512M,文件系统格式为ext2
1 | dd if=/dev/zero of=virtsd.ext2 bs=1M count=512 |
制作好的目标sd卡是virtsd.ext2
2.将整个文件系统目录拷贝到sd卡中
1 | mount -t ext2 virtsd.ext2 /mnt -o loop |
3.通过qemu虚拟机启动内核,设定机器为vexpress-a9 加载程序为内核zImage, sd卡为virtsd.ext2 内存为256M,不需要图形界面,设定根文件系统存储设备为/dev/mmcblk0
1 | qemu-system-arm -M vexpress-a9 -m 512M -kernel zImage -dtb vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd virtsd.ext2 |
退出qemu虚拟机
1.直接退出:先按ctrl+a
,然后松开,按x键退出虚拟机
2.系统正常关机:在虚拟机shell中执行poweroff
命令即可
网络配置
qemu有多种网络模式,在这里以TUN/TAP设备虚拟网卡设置为例说明其配置过程。
先决条件
基于TUN/TAP设备的配置需要宿主机插入tun模块的支持,如果没有该模块的支持则不能顺利实施网桥模式的设置,此外,为了配置虚拟机访问外网,还需要内核开启iptables的支持,并且需要用户空间iptables管理工具。
可以通过下面的命令查看是否加载了tun模块,如果没有加载,可以先网络搜索加载方法先将其加载:
1 | lsmod | grep tun |
如果加载了tun模块,该命令将得到类似下面的输出:
1 | tun 28672 2 |
同时,需要确定tun模块的设备文件是否已经创建,其路径是/dev/net/tun。
1 | ls /dev/net/ |
如果没有该设备文件,可以通过下面的命令创建该设备文件:
1 | mkdir /dev/net |
配置网络
宿主机虚拟网卡配置
在主机中创建/etc/qemu-ifup文件,该文件用来在qemu虚拟机启动的时候创建一个虚拟网卡,文件内容如下:
1 | !/bin/bash |
然后启动虚拟机,并传入网络参数-net nic,vlan=0 -net tap,vlan=0
,结合直接从SD卡启动系统内核小节的启动参数,加入网络参数后的完整的启动命令如下:
1 | qemu-system-arm -M vexpress-a9 -m 512M -kernel zImage -dtb vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd virtsd.ext2 -net nic,vlan=0 -net tap,vlan=0 |
启动虚拟机后同时在宿主机上创建了tap0虚拟网卡,其ip地址为172.0.0.1
。
虚拟机网络配置
在虚拟机中配置eth0网卡和宿主机虚拟网卡tap0在同一网段:
1 | ifconfig eth0 172.0.0.2 netmask 255.255.0.0 |
此时,虚拟机即可和宿主机上的tap0网卡互通,可以通过ping命令测试。
配置虚拟机的默认网关地址为宿主机tap0的地址:
1 | route add default gw 172.0.0.1 |
此时,可以通过虚拟机访问到宿主机eth0的网段了。可以通过ping命令测试。
虚拟机外网访问配置
首先通过下面的命令在宿主机上开启路由转发功能:
1 | echo 1 > /proc/sys/net/ipv4/ip_forward |
然后添加nat转换规则:
1 | iptables -t nat -A POSTROUTING -o eth0 -s 172.0.0.0/24 -j MASQUERADE |
此时即可通过虚拟机访问互联网,可以通过ping一些公网的ip地址进行测试。