一、背景
手头没有合适的arm64开发板,但是需要arm的环境,于是想到qemu模拟一个。除了硬件交互以外,软件层面的开发还是都可以实现的。
虚拟机还能自定义内存大小和镜像大小,非常适合上板前的验证,合适的话再买也不迟。
为了提高配置效率与实用性,我直接使用香橙派的构建脚本编译根文件。没有和其他作者一样使用busybox制作根文件系统,我使用debian来提供apt等常用功能。
二、构建流程
1、qemu
建议使用git clone 源码进行编译,减少匹配问题。
bash
git clone https://github.com/qemu/qemu.git
或者官网压缩包下载地址:
Download QEMU - QEMUhttps://www.qemu.org/download/
以 git clone 为例编译源码:
bash
cd qemu
./configure --enable-kvm --enable-debug --enable-vnc --enable-user --enable-slirp --target-list="aarch64-softmmu"
make -j8
sudo make install
需要注意配置源码时的configure指令,如果想要了解参数的配置见我另一篇文章。
2、交叉编译工具链
可以使用apt安装,也可以下载arm官方的压缩包,解压后记得配置PATH环境变量。
bash
sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev build-essential git bison flex libssl-dev
3、kernel
内核源码我使用香橙派提供的6.1版本,使用Linux官方的也是可以的。
bash
git clone --depth=1 -b orange-pi-6.1-rk35xx https://github.com/orangepi-xunlong/linux-orangepi
设置ARCH和CORSS_COMPILE。
bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
内核config配置文件建议使用defconfig,使用rockchip的默认配置无法通过qemu启动,暂时不知道原因。对于qemu的虚拟机,不需要设备树,只需要镜像即可。
bash
make defconfig
make Image -j8
4、debian根文件
使用香橙派的 orangepi-build 工具构建,这里只是简单介绍过程,详细流程可以查看任意开发板的使用手册。
下载工具:
bash
git clone https://github.com/orangepi-xunlong/orangepi-build.git -b next
下载完成后,执行build脚本:
bash
sudo ./build
选择构建Rootfs and all deb packages:
然后需要选择开发板类型,看心情选即可。
下一步是选择内核版本,由于内核选的6.1,这里需要选current。如果内核选择5.4,这里要选legacy。
选择rootfs类型,看需要选即可:
选择服务器版本还是桌面版本,建议服务器版本:
选择标准:
这一步回车跳过:
等待构建完成可以在**external/cache/rootfs/**目录下找到 lz4 压缩包,需要解压后创建一个可挂载的ext4文件镜像。
(1)创建一个空的磁盘镜像文件
bash
qemu-img create -f raw rootfs.img 5G
(2)格式化磁盘镜像
bash
sudo mkfs.ext4 rootfs.img
(3)挂载磁盘镜像
bash
sudo mkdir -p /mnt/rootfs
sudo mount -o loop rootfs.img /mnt/rootfs
(4)将解压后的内容直接写入到挂载的目录
压缩包替换为生成的文件名。
bash
sudo lz4 -d --stdout bookworm-cli-arm64.tar.lz4 | sudo tar -x -C /mnt/rootfs/
(5)卸载磁盘镜像
bash
sudo umount /mnt/rootfs
三、启动虚拟机
创建一个工作目录 qemu_arm64,将生成的内核镜像,根文件系统放到目录。创建一个文件夹share_dir用于共享文件。
新建启动脚本 start.sh:
bash
#!/bin/bash
sudo qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-nographic \
-smp 4 \
-m 2048 \
-kernel Image \
-drive if=none,file=rootfs.img,format=raw,index=0,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-append "rdinit=/linuxrc root=/dev/vda rw console=ttyAMA0 loglevel=8" \
-virtfs local,path=./share_dir,mount_tag=host0,security_model=passthrough,id=host0 \
-netdev user,id=net0,hostfwd=tcp::10022-:22 \
-device virtio-net-pci,netdev=net0
加sudo主要是用于隔离环境变量,防止意外。参数的大部分可以通过chatgpt分析,其中用于挂载根文件的指令:
bash
-drive if=none,file=rootfs.img,format=raw,index=0,id=hd0 \
-device virtio-blk-device,drive=hd0 \
用于挂载共享文件的指令:
bash
-virtfs local,path=./share_dir,mount_tag=host0,security_model=passthrough,id=host0 \
网络我没有使用桥接的方式,而是使用qemu自带的user方式联网,缺点是不能和宿主机通信。用于连接网络的指令:
bash
-netdev user,id=net0,hostfwd=tcp::10022-:22 \
-device virtio-net-pci,netdev=net0
执行start.sh脚本即可启动虚拟机:
四、挂载和联网
启动虚拟机后,需要挂载共享文件夹,并根据模拟的网卡进行dhcp。
1、挂载共享文件夹
bash
#!/bin/bash
mkdir -p /mnt/share
mount -t 9p -o trans=virtio,version=9p2000.L,access=any,rw host0 /mnt/share
这里的文件夹就是启动指令中的share_dir,宿主机可以将文件放到文件夹内,虚拟机可以读和访问,但是不能写,提示没有权限,暂时还未解决。
不过在网络设置成功后,可以使用scp命令传输文件到宿主机,形成数据回环。
2、dhcp
bash
#!/bin/bash
INTERFACE=$(ls /sys/class/net/ | grep -v lo)
sudo ip link set dev $INTERFACE up
sudo dhclient $INTERFACE
这个脚本我采用临时设置的方式,原因是虚拟机有时候网卡名字会变,直接写入网络配置文件可能会失效。执行后会查询当前的网卡名,然后执行dhcp。
获取到ip后就可以apt更新了。
五、其他
如果使用busybox构建最小根文件系统,在编译根文件时交叉编译器最好使用arm官方原生的,使用其他第三方定制的工具链会引发内核崩溃。血的教训!排列组合三天才找到问题。