Qemu可以对许多架构的CPU或开发板进行模拟。在我们没有开发板的时候,可以通过其进行一些开发板的模拟,便于学习。
安装sudo apt-get install qemu sudo apt install qemu-utils # 使用不同架构的CPU,这个安装方式要再次安装相应命令 sudo apt install qemu-system-x86 sudo apt install qemu-system-arm sudo apt install qemu-system-mips
如果需要源码(源码好像可以安装全部指令):
git clone git://git.qemu-project.org/qemu.git 或者 wget http://wiki.qemu-project.org/download/qemu-2.0.0.tar.bz2使用
创建镜像文件
qemu-img create -f qcow2 test-vm-1.qcow2 10G
-f : 选项用于指定镜像的格式
qcow2 : 格式是 Qemu 最常用的镜像格式,采用来写时复制技术来优化性能
test-vm-1.qcow2 : 是镜像文件的名字
10G:镜像文件大小
镜像文件创建完成后,可使用 qemu-system-x86 来启动x86 架构的虚拟机(当然,其他架构也有相应的命令):
qemu-system-x86_64 test-vm-1.qcow2
因为没有安装操作系统,会出现下面的界面:
安装镜像命令:
qemu-system-x86_64 -boot d -cdrom ubuntu-16.04.7-desktop-amd64.iso -hda test-vm-1.qcow2 -m 2048
当然也可以把hda选项去掉仅仅做测试。
其实不仅仅是x86-x64的架构的镜像,像arm等体系架构都可以使用qemu来模拟,不过命令不相同。
树莓派1代树莓派CPU是arm架构的,所以下面使用arm相关的启动指令。
下载地址:
https://github.com/dhruvvyas90/qemu-rpi-kernel
实际使用两个文件:kernel-qemu-4.19.50-buster 、 versatile-pb-buster.dtb。前者是1代内核镜像,后者是versatile开发板类型的设备树(没有树莓派1代的开发版版本,这里用来替代,设备树描述的就是开发板的硬件信息)。
img镜像下载地址:
http://downloads.raspberrypi.org/raspbian_lite/images/
这里选择在2019-09-30目录下面的镜像,下载压缩包解压后得到2019-09-26-raspbian-buster-lite.img。
启动命令:
qemu-system-arm
-M versatilepb
-cpu arm1176
-drive format=raw,file=./pi/2019-09-26-raspbian-buster-lite.img
-net nic
-net user,hostfwd=tcp::5022-:22
-dtb ./pi/versatile-pb-buster.dtb
-kernel ./pi/kernel-qemu-4.19.50-buster
-append "root=/dev/sda2 panic=1 rootfstype=ext4 rw"
-serial stdio
-usb
qemu-system-arm -M versatilepb -cpu arm1176 -m 256 -hda ./pi/2019-09-26-raspbian-buster-lite.img -net user,hostfwd=tcp::5022-:22 -dtb ./pi/versatile-pb-buster.dtb -kernel ./pi/kernel-qemu-4.19.50-buster -append 'root=/dev/sda2 panic=1' -no-reboot
上面两条指令都可以正常启动系统,但是使用时会有些区别,感兴趣的可以自己测试一下。
下面以第一条指令的参数进行解释:
| 参数 | 说明 |
|---|---|
| -M versatilepb | 模拟的电路板,versatilepb |
| -cpu arm1176 | cpu型号 |
| -m 256 | 内存256M |
| -drive format=raw,file= | 加载的镜像格式和位置 |
| -net user,hostfwd=tcp::5022-:22 | 网卡使用NAT模式,将主机的5022映射到虚拟机22口便于SSH |
| -dtb | 设备树文件 |
| -kernel | |
| -append | 附加命令,内核命令行 |
| -serial stdio | 将CMD命令行用作标准输入输出,可以通过CMD命令行对虚拟机发送命令,而不必使用虚拟机的显示和输入 |
| -usb | 支持usb |
默认下载(或qemu官方)的qemu内置支持的开发板没有龙芯的,需要使用龙芯提供的。
龙芯开放的Qemu源代码 : https://gitee.com/loongsonlab/qemu
安装:
cd/ mkdir ./build cd ./build ../configure --target-list=mipsel-softmmu,mips64el-softmmu --disable-werror make
编译依赖库:
Python libpixman-1-dev
编译时链接报了stime未定义的错误,解决办法:
将 return get_errno(stime(&host_time)); 修改为: return get_errno(clock_settime(CLOCK_REALTIME, &host_time));
运行pmon,注意下面的pmon镜像是最终的(这里包含设备树,否则无法正常启动):
./mips64el-softmmu/qemu-system-mips64el
-M ls2k
-m 256
-smp 1
-serial stdio
-bios ../../../ls2k/gzrom-dtb.bin
-net nic
-nic user,net=192.168.1.55/24,host=192.168.1.5,tftp=/srv/tftp
运行结果:
简单的测试指令:
@todo 制作内核镜像、文件系统启动操作系统。
- 内核下载和编译
# 安装交叉编译链工具,有的话直接引入或忽略 # 32位 sudo apt install gcc-arm-linux-gnueabi # 64位 sudo apt-get install gcc-aarch64-linux-gnu # 获取内核镜像,其它版本也可以,一般都有vexpress的默认配置,速度慢的话可以windows下直接访问网站下载 wget https://mirror.bjtu.edu.cn/kernel/linux/kernel/v5.x/linux-5.10.tar.xz # 解压 tar xf linux-5.10.tar.xz cd linux-5.10 # 配置,要指定ARCH=arm(也可以设置环境变量),才会从arm架构中找配置文件 make ARCH=arm vexpress_defconfig # 编译,可以使用参数zImage,modules,dtbs编译指定的部分,默认全部编译 make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm -j8 # 生成的文件位于arch/arm/boot下
# qemu单独启动kernel # 因为没有文件系统会报错 qemu-system-arm -M vexpress-a9 -m 512M -kernel ./arch/arm/boot/zImage -dtb ./arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
- 文件系统下载和制作
# busybox下载 wget https://busybox.net/downloads/busybox-1.32.0.tar.bz2 # 解压 tar jxf busybox-1.32.0.tar.bz2 cd busybox-1.32.0 # 使用默认配置 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig # 编译 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4 # 安装,默认安装当前的__install文件夹下 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install # 至此文件系统的编译已完成,剩下的任务是设置文件系统的内容 # 建立文件夹存放操作系统,然后拷贝需要的文件到里面 mkdir rootfs cp -ra _install/* rootfs/ # (里面的 *.a 其实可以删掉,只用 .so) mkdir -p rootfs/lib cp -ra /usr/arm-linux-gnueabi/lib/* rootfs/lib/ cd rootfs # 全是空文件夹 mkdir dev proc sys tmp root var mnt # 制作设备节点,一些设备的节点号是固定的,参看 ubuntu 主机中的 cd dev sudo mknod -m 666 tty1 c 4 1 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
- 制作根文件系统镜像
# 有点不明白,感觉直接把文件当成一个磁盘来用了,待研究 # 新建一个文件夹进行操作 # 64M空间,空间不够也可以把它改大点 dd if=/dev/zero of=a9rootfs.ext3 bs=1M count=64 mkfs.ext3 a9rootfs.ext3 sudo mount -t ext3 a9rootfs.ext3 /mnt -o loop sudo cp -ra ../busybox-1.32.0/rootfs/* /mnt sudo chown -R root.root /mnt/* sudo umount /mnt
qemu-system-arm
-M vexpress-a9
-m 512M
-kernel ./arch/arm/boot/zImage
-dtb ./arch/arm/boot/dts/vexpress-v2p-ca9.dtb
-nographic
-append "init=/linuxrc root=/dev/mmcblk0 rw console=ttyAMA0"
-sd ../rootfs/a9rootfs.ext3
# 报错:end Kernel panic - not syncing: Requested init /linuxrc failed (error -8) # 查看错误码-8,为执行文件格式错误,文件系统bin下程序在x64虚拟机上也可以运行,原因是编译时未指定交叉编译链 # 激动!!!重新编译后,可以正常启动,进到终端 # 文件系统还可以再优化
# 图形界面启动
qemu-system-arm
-M vexpress-a9
-m 512M
-kernel ./arch/arm/boot/zImage
-dtb ./arch/arm/boot/dts/vexpress-v2p-ca9.dtb
-append "init=/linuxrc root=/dev/mmcblk0 rw console=tty0"
-sd ../rootfs/a9rootfs.ext3
- u-boot编译
wget https://ftp.denx.de/pub/u-boot/u-boot-2022.04.tar.bz2 tar xjf u-boot-2022.04.tar.bz2 cd u-boot-2022.04 make ARCH=arm vexpress_ca9x4_defconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- all # 报错:fatal error: openssl/evp.h: No such file or directory # 安装下面的库 sudo apt-get install libssl-dev # 生成的u-boot为可执行文件,u-boot.bin为二进制文件
# 单独启动u-boot qemu-system-arm -M vexpress-a9 -m 512M -kernel ./u-boot -nographic
# 制作多分区的SD卡(文件模拟的) # 这里要使用uImage进行启动,所以内核要重新编译一下 # uImage也可以用mkimage工具生成,下面的命令其实也是用mkimage工具 # uImage是u-boot的专用的启动镜像,可以由不同格式的u-boot镜像通过工具生成,只是加了一个文件头便于启动 sudo apt install u-boot-tools make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm LOADADDR=0x60003000 uImage -j8 # 一个分区存放kernel,一个分区存放镜像 # 前面分区放置文件系统镜像的时候其实是创建回环设备,用文件模拟块设备 # 使用losetup可以查看所有的回环设备,我们把它直接当成SD卡就行 # 这里只是把文件名字叫做SD,可以用任何名字 # dd if=/dev/zero of=SD bs=1M count=64 # 回环设备名字不能重复,使用未使用的名字 # sudo losetup /dev/loop15 SD dd if=/dev/zero of=SD bs=1M count=64 sudo parted SD --script -- mklabel msdos # 这里和2048对齐一下,减少一个警告 sudo parted SD --script -- mkpart primary fat32 2048s 40956s sudo parted SD --script -- mkpart primary ext4 40960s -1 # 建立映射,然后格式化两个分区 sudo losetup --show /dev/loop15 SD # sudo apt install kpartx sudo kpartx -va /dev/loop15 sudo mkfs.vfat /dev/mapper/loop15p1 sudo mkfs.ext4 /dev/mapper/loop15p2 # 拷贝内核zImage、vexpress-v2p-ca9.dtb到第一个分区 sudo mount /dev/mapper/loop15p1 /mnt sudo cp ../linux-5.10/arch/arm/boot/uImage /mnt sudo cp ../linux-5.10/arch/arm/boot/dts/vexpress-v2p-ca9.dtb /mnt # 拷贝文件系统内的所有文件到第二个分区 sudo mount /dev/mapper/loop15p2 /media sudo cp -r ../busybox-1.32.0/rootfs/* /media sudo chown -R root.root /media/* # 卸载 sudo umount /mnt sudo umount /media
# u-boot启动SD卡中的kernel和文件系统 qemu-system-arm -M vexpress-a9 -m 512M -kernel ./u-boot-2022.04/u-boot -nographic -append "root=/dev/mmcblk0 console=ttyAMA0" -sd ./rootfs/SD # 启动 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 # tips 这里对文件系统的修改会保存
# u-boot tftp网络启动内核 qemu-system-arm -M vexpress-a9 -m 512M -kernel ./u-boot-2022.04/u-boot -nographic -append "root=/dev/mmcblk0 console=ttyAMA0" -sd ./rootfs/SD -net nic -nic user,net=192.168.1.55/24,host=192.168.1.5,tftp=/srv/tftp # 启动 # 测试网络 setenv ipaddr 192.168.1.56 ping 192.168.1.5 # tftpboot 加载镜像 setenv serverip 192.168.1.5 tftpboot 60003000 uImage tftpboot 60500000 vexpress-v2p-ca9.dtb setenv bootargs 'init=/linuxrc root=/dev/mmcblk0p2 rw rootwait earlyprintk console=ttyAMA0' bootm 60003000 - 60500000 # 其它 内核下使用ifconfig时提示缺少文件,创建即可(可以使用touch命令),然后直接设置eth0网卡的ip,就应该和主机之间就可以进行网络通信了。
说明:无论是从SD卡启动,还是网络启动,过程都是一样的,都是将内核镜像加载到内存,然后启动(其它启动方式大同小异),区别在于下载内核镜像的位置。这里文件系统在bootargs里面指定,位于SD卡中,也可以使用网络挂载文件系统。
错误记录1.fatal error: linux/compiler-gcc9.h: No such file or directory
原因:gcc版本和内核版本不匹配
解决办法:
更换内核版本
2. multiple (or no) load addresses: This is incompatible with uImages
解决办法:
指定LOADADDR参数
3. “mkimage” command not found
安装u-boot-tools
4. 卡在Start Kernel …
修改生成uImage时的LOADADDR地址为0x60003000,为什么是这个不清楚。
- windows 10 下使用虚拟机Linux ubuntu 5.11.0-27-generic进行测试。
- QEMU仿真树莓派1和3B-保姆级教程
- QEMU 3.1.0安装手记
- 龙芯杯无开发板如何开发和调试linux
- qemu模拟arm嵌入式环境
- Ubuntu通过apt-get安装指定版本和查询指定软件有多少个版本
- 搭建基于qemu的仿真环境与应用
- Kernel panic - not syncing: Requested init /linuxrc failed (error -13)
- mount -o loop 解释
- linux创建回环设备的方法



