qemu嵌入式环境

00. 编译qemu

安装依赖,主要是交叉工具链,编译uboot、kernel时用(我ubuntu主机中有一个for arm-cortex-a64的交叉编译工具链aarch64, 有一个for arm-cortex-m的交叉编译工具链, 可以在docker中安装一个 for arm-cortex-a32 的交叉编译工具链 arm-linux-guneabi-gcc)

 apt-get update
 apt-get install vim bc build-essential gcc-arm-linux-gnueabi libncurses5-dev bison flex

下载qemu源码(我下的是v2.8.0,原来是想下载v5.2.0,后来发现网络连接总是配置不好,v2.8.0 的 configure需要python2)

 #不加 --target-list 则编译支持的所有平台, --enable-debug 允许gdb qemu
 ./configure --enable-kvm --enable-debug --prefix=/opt/qemu --target-list="arm-softmmu x86_64-softmmu i386-softmmu aarch64-softmmu"
 make -j4
 sudo make install

最后记得将 /opt/qemu/bin/ 添加到 bashrc

01. 关于qemu

qemu-system-arm 会在 x86-ubuntu(我们的真实机器)中 仿真arm

 qemu-system-arm --version   #打印版本
 qemu-system-arm -M help     #打印目前所有支持的machine

-M help 输出中有 "vexpress-a9 ARM Versatile Express for Cortex-A9" 这款arm评估板,关于开发板的资料 http://www.myir-tech.com/download.asp?nid=49 。关于 arm 其他的 ip核, https://developer.arm.com/documentation/#sort=relevancy 。组成cortex-A系列最小可运行系统的硬件

  • 1.CPU

  • 2.DDR/SDRAM (sd / emmc / nand or nor flash / net / ... 均不为必须,但要有一个)

  • 3.uart (控制输入)

  • 4.lcd (显示输出,其实也不是必须)

1. 准备uboot

1.1 下载uboot

 git clone https://github.com/u-boot/u-boot.git  #或者去 https://ftp.denx.de/pub/u-boot 下载 u-boot-2017.07.tar.bz2
 git tag
 git checkout v2020.10

1.2 make uboot

可以在make之前修改 include/configs/vexpress_common.h 中的 CONFIG_BOOTCOMMAND 指定uboot启动行为,这样就不用后面进入后每次敲命令了(如果saveenv不可用的话)。

 vim config.mk,  #ARCH := arm (这里还是export ARCH=arm 最保险)
 vim Makefile,   #CROSS_COMPILE ?= arm-linux-gnueabi-
 make vexpress_ca9x4_defconfig
 make -j4

2. 准备kernel

2.1 下载kernel source

 #或者直接去https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/ 下载,eg. linux-4.19.168.tar.xz
 git clone https://github.com/torvalds/linux.git
 git tag
 git checkout v4.15-rc9

在 linux/arch/arm/下, mach-vexpress 就是对应 vexpress machine, configs/vexpress_defconfig 就是其内核配置文件。回到 linux顶层目录,

2.2 编译kernel(for vexpress)

 vim Makefile,   #修改 arch ?= ARM, CROSS_COMPILE ?= arm-linux-gnueabi-
 #或者这里 export ARCH=arm, export CROSS_COMPILE=arm-linux-gnueabi-
 #或者下面make时都加上 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
 ​
 make vexpress_defconfig #配置内核,这之后还可以make menuconfig微调
 make zImage -j4         #编译内核
 make modules -j4        #编译内核模块
 make dtbs               #编译device tree,生成.dts
 ​
 #使用uboot引导kernel,要编译uImage(zImage与位置无关,uImage是专门给uboot准备的,前面加上了0x40长度的header,使用uboot/tool/mkimage制作得到)
 make LOADADDR=0x60003000 uImage -j4 #若提示mkimage找不到,则将 /mnt/home/fang/codes/u-boot/tools 添加到PATH

3. 制作rootfs

rootfs可以放在 net(网络接口,eg.nfs/tftp), nand/nor flash, sd/emmc(mmc接口), hard disk 中, 通常包含了 1.各种命令(shell) + 库(各种用途的库),2.字符设备等设备文件(驱动接口)3.配置脚本。busybox / buildroot 都是制作rootfs的工具.(制作完成我们再搞一个磁盘映像 or 拷贝到物理设备即可)。

3.1 下载busybox source并编译https://busybox.net/downloads/ 下载 busybox源码,解压(下面几步骤在docker环境中build)

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

3.2 制作rootfs内容

 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/

3.3 制作磁盘映像并将数据拷贝到其中

 dd if=/dev/zero of=vexpress.img bs=1M count=32
 mkfs.ext3 vexpress.img
 mount -t ext3 vexpress.img /mnt -o loop
 cp -ra rootfs/* /mnt
 umount /mnt

这里也附上制作多分区磁盘映像的方法

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

然后拷贝rootfs到ext4分区。完成后umount并删除本地映射:

 sudo umount aa
 sudo umount bb
 ​
 sudo kpartx -d /dev/loop0
 sudo losetup -d /dev/loop0
 ​
 rm -rf aa bb

4. qemu启动

4.1 qemu直接启动kernel

qemu提供了直接启动kernel的机制,可以不用第三方的bootloader。

 #-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)
 ​
 qemu-system-arm \
     -M vexpress-a9 \
     -m 512M \
     -kernel /home/fang/codes/linux/arch/arm/boot/zImage \
     -dtb /home/fang/codes/linux/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
     -nographic \
     -append "root=/dev/mmcblk0 rw console=ttyAMA0" \
     -sd vexpress.img

4.2 qemu启动uboot,uboot启动kernel

 

 qemu-system-arm \
     -M vexpress-a9 \
     -m 512M \
     -kernel /home/fang/codes/u-boot/u-boot \
     -nographic

接下来一个重要的事情需要搞清楚, qemu启动加载uboot后, uboot如何加载内核? 内核又如何知道rootfs存放位置?

1. uboot 加载 kernel 可以通过 tftp 从 ubuntu主机拿到。或者从sd卡拿,但是sd卡已经存放了rootfs,没法和kernel image放一起,所以制作sd卡映像的时候需制作两个分区,一个放kernel+dtb(分区格式只要uboot认识即可),另一个放rootfs(必须是linux指定的ext格式)

2. 使用sd卡映像的方式操作起来简单,不需要网络。但是debug不方便。所以还是要有qemu和ubuntu主机网络通信的方法,不管是tftp kernel+dtb,还是kernel后面挂载nfs文件系统都比较方便debug。qemu可以通过tap实现与ubuntu互联

3. uboot如何加载内核的问题,可以在uboot中指定 bootcmd 环境变量。

4. 内核去哪挂载怎么挂载rootfs的问题,可以在uboot中设置bootargs(就是挂载rootfs时的参数),bootargs指示kernel起来后通过从mmcblock0p2获取rootfs,而qemu连接一个sd卡(mmc接口), 内容 xx.img 正是rootfs映像(或者nfs方式获取rootfs)

 

4.2.1 通过sd卡第一个分区获取kernel,第二分区获取rootfs

按照2.3节,制作含有两个分区的镜像,然后拷贝zImage uImage xx.dtb 到 fat分区,拷贝rootfs到ext4分区。uImage是专门给uboot用的make的时候要给 LOADADDR,并且uboot/tools/mkimage工具要添加到PATH。(但是看起来uboot可以使用bootz来启动zImage)。完成后启动qemu:

 sudo /opt/qemu/bin/qemu-system-arm \
     -M vexpress-a9 \
     -m 512M \
     -kernel ../u-boot-2017.07/u-boot \
     -nographic \
     -sd abc.img

uboot起来后

 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 打开早期打印?)

4.2.2 通过tftp获取kernel, mmc获取rootfs

ubuntu和qemu打通网络,ubuntu需要配置好tap设备( ref: https://blog.csdn.net/aggresss/article/details/54948143),

 sudo apt-get install uml-utilities              #User-Mode Linux,使用它创建TAP
 sudo tunctl -u root -t tap30                    #在主机上创建一个网络设备
 sudo ifconfig tap30 192.168.111.1 promisc up    #配置网卡IP地址,并且混杂模式启用

顺便在ubuntu主机安装tftp服务,并配置

 sudo apt-get install tftp-hpa tftpd-hpa xinetd
 sudo vim /etc/default/tftpd-hpa ,
     TFTP_USERNAME="tftp"
     TFTP_DIRECTORY="/home/fang/tftpfiles"
     TFTP_ADDRESS="0.0.0.0:69"
     TFTP_OPTIONS="-l -c -s"
 mkdir -p /home/fang/tftpfiles, 并 chmod 777 tftpfiles
 /etc/init.d/tftpd-hpa restart
 ​
 cp /home/fang/codes/linux/arch/arm/boot/dts/vexpress-v2p-ca9.dtb ~/tftpfiles/
 cp /home/fang/codes/linux/arch/arm/boot/uImage ~/tftpfiles/
 cp /home/fang/codes/linux/arch/arm/boot/zImage ~/tftpfiles/

qemu这边则指定好启动参数(就是虚拟的硬件配置)。参照博客的命令似乎不能识别 vlan 选项,原来是qemu v5.2.0新版本使用 -device 和 -netdev.(https://www.qemu.org/2018/05/31/nic-parameter/)。但是更改了启动命令如下,发现起来后还是无法使用网络设备。推断是qemu的配置还是没有搞好,鉴于大部分资料是都是关于使用 -net方式,所以无奈换为qemu v2.8.0。编译安装步骤和上面 00节 一样。最后的启动命令就是下面这个(需要配置好etc下的文件,不然ifconfig等命令无法使用。可以参照正点原子alpha驱动里关于busybox的章节):

 sudo /opt/qemu/bin/qemu-system-arm \
     -M vexpress-a9 \
     -m 512M \
     -kernel ../u-boot-2017.07/u-boot \
     -nographic \
     -sd vexpress2.img \
     -net nic,vlan=0 -net tap,vlan=0,ifname=tap30,script=no,downscript=no

qemu启动uboot后,在uboot里设置一下环境参数

 setenv ipaddr 192.168.111.2
 #setenv ethaddr 00:04:9f:04:d2:35
 setenv gatewayip 192.168.111.1
 setenv netmask 255.255.255.0
 setenv serverip 192.168.111.1
 setenv bootargs 'init=/linuxrc root=/dev/mmcblk0p2 rw rootwait earlyprintk console=ttyAMA0'
 saveenv

我把rootfs放在了mmc接口设备的第二个分区上了,所以root=/dev/mmcblk0p2(表示第0个mmc设备的第2个分区)。然后使用tftp命令拿kernel+dtb:

 tftp 60003000 uImage
 tftp 60500000 vexpress-v2p-ca9.dtb
 bootm 60003000 - 60500000

进入系统后 ifconfig -a 应该可以看到多出来一个eth0,我们配置一下ip

 ifconfig eth0 192.168.111.2 promisc up
 ping 192.168.111.1  #ok

 

4.2.3 通过tftp获取kernel, nfs获取rootfs

大体上和4.2.2是一样的,不同在于

setenv bootargs 'console=ttyAMA0 root=/dev/nfs nfsroot=192.168.111.1:/home/fang/nfs_root,proto=tcp rw ip=192.168.111.2:192.168.111.1:192.168.111.1:255.255.255.0::eth0:off'

同时ubuntu主机上安装nfs服务(参考正点原子的nfs搭建):

sudo apt-get install nfs-kernel-server
sudo vim /etc/exports

#rwsync之间没有空格
/home/fang/nfs_root *(rw,sync,no_root_squash)

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

 

---------------------------------------------------------------------------------------

qemuv5.2.0失败的例子

 #sudo /opt/qemu/bin/qemu-system-arm \
 #    -M vexpress-a9 \
 #    -m 512M \
 #    -kernel /home/fang/codes/linux/arch/arm/boot/zImage \
 #    -dtb /home/fang/codes/linux/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
 #    -nographic -append "init=/linuxrc root=/dev/mmcblk0 rw rootwait earlyprintk console=ttyAMA0" \
 #    -sd vexpress.img \
 #    -device virtio-net-device,netdev=dev0,mac='00:00:00:01:00:01' \
 #    -netdev tap,ifname=tap30,id=dev0,script=no,downscript=no
 ​
 #1.注意是sudo 了,不然貌似没法 access tap设备
 #2.给kernel的参数中,添加了 init=/linuxrc, rootwait, earlyprintk 
 #3.我完善了etc中的文件,不然没法使用 ifconfig 等命令(看来网卡启动还需要etc中的一些配置)
 #4.-device virtio-net-device, 这个名字不是随便取的,使用下面的方法查看
 #  qemu-system-arm -M vexpress-a9 -device help
 #  pci的设备好像没法使用,所以我在 net device中找了一个不使用pci bus的,就是 virtio-net-device

https://www.cnblogs.com/microxiami/p/11093276.html

https://www.qemu.org/docs/master/ https://wiki.qemu.org/Documentation https://www.cnblogs.com/bakari/p/7858029.html

https://github.com/aggresss/LKDemo 主板:https://www.arm.com/zh/products/tools/development-boards/versatile-express/motherboard-express.php 处理器子板:https://www.arm.com/zh/products/tools/development-boards/versatile-express/coretile-express.php 文档下载:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.set.boards/index.html

# https://www.bilibili.com/video/BV1NJ411m7Ah/?spm_id_from=333.788.recommend_more_video.0

#深入uboot https://aggresss.blog.csdn.net/article/details/52753098 #基于 qemu 的 android 嵌入式全栈 https://blog.csdn.net/aggresss/category_6542756.html

<<from 51 to linux>> https://www.zhihu.com/column/c_1141746317452808192

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值