利用Qemu-4.0虚拟ARM64实验平台

在无aarch64开发板的情况下,为了能够学习实验aarch64相关的代码,通过Qemu模拟器搭建aarch64运行环境,这样就能完全达到实验学习的目的,既节省了硬件成本又便于实验操作。本文记录一下环境的搭建过程和小坑,方便以后自己快速搭建调试环境。

交叉编译工具

1
2
3
wget http://releases.linaro.org/components/toolchain/binaries/7.2-2017.11/aarch64-linux-gnu/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz
mkdir toolchains
tar -xJf aarch64-linux-gnu/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz -C toolchains
  • 修改环境变量PATH,使工具方便使用

    1
    vim ~/.bashrc
  • 在文件末尾添加如下内容

    1
    export PATH=$PATH:/path/to/toolchains/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu/bin

    注意:根据实际情况,确保路径设置正确

  • 验证

    1
    aarch64-linux-gnu-gcc -v

    在终端会输出aarch64-linux-gnu-gcc的配置及版本信息

构建QEMU

1
2
3
4
5
6
wget https://download.qemu.org/qemu-4.0.0.tar.xz
tar xvJf qemu-4.0.0.tar.xz
cd qemu-4.0.0
./configure --target-list=x86_64-softmmu,x86_64-linux-user,arm-softmmu,arm-linux-user,aarch64-softmmu,aarch64-linux-user --enable-kvm
make
sudo make install

编译Kernel

1
2
3
4
5
6
7
8
9
10
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.19.27.tar.xz
mkdir build
tar xJf linux-4.19.27.tar.xz
cd linux-4.19.27
make ARCH=arm64 defconfig O=../build/ CROSS_COMPILE=aarch64-linux-gnu-

# 如果需要调整配置选项,则使用menuconfig
make ARCH=arm64 menuconfig O=../build/ CROSS_COMPILE=aarch64-linux-gnu-

make ARCH=arm64 Image -j8 O=../build/ CROSS_COMPILE=aarch64-linux-gnu-

模拟运行

1
qemu-system-aarch64 -kernel build/arch/arm64/boot/Image -append "console=ttyAMA0" -m 2048M -smp 4  -M virt -cpu cortex-a57 -nographic
  • -m 指定内存大小
  • -M 指定虚拟机器「machine」的类型
  • -cpu 指定虚拟CPU的型号
  • -smp 指定对称多处理的核心数
  • -append 指定内核启动时使用的命令行参数「cmdline」

output

图1 无根文件系统的情况下模拟运行的输出

由于缺少根文件系统,虚拟aarch64平台还未能正常启动。
按Ctrl+A+X组合键退出qemu模拟器
按Ctrl+A+C组合键进入qemu-monitor,输入help可以查看操作命令

定制rootfs

ubuntu-base-18.04.1

  • 安装Qemu用户模拟器(静态编译版)

    1
    sudo apt-get install qemu-user-static binfmt-support
  • 检验

    1
    update-binfmts --display

    update-binfmts

    图2 运行update-binfmts的输出

制作步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
wget http://cdimage.ubuntu.com/ubuntu-base/releases/18.04.2/release/ubuntu-base-18.04.2-base-arm64.tar.gz

mkdir rootfs
dd if=/dev/zero of=ubuntu-18.04-rootfs_ext4.img bs=1M count=1024 oflag=direct
mkfs.ext4 ubuntu-18.04-rootfs_ext4.img
sudo mount -t ext4 ubuntu-18.04-rootfs_ext4.img rootfs/
sudo tar -xzf ubuntu-base-18.04.1-base-arm64.tar.gz -C rootfs/

sudo cp /usr/bin/qemu-aarch64-static rootfs/usr/bin/
sudo cp /etc/resolv.conf rootfs/etc/resolv.conf
sudo mount -t proc /proc rootfs/proc
sudo mount -t sysfs /sys rootfs/sys
sudo mount -o bind /dev rootfs/dev
sudo mount -o bind /dev/pts rootfs/dev/pts

# 安装内核模块(可选)
cd linux-4.19.27
make ARCH=arm64 modules -j8 O=../build/ CROSS_COMPILE=aarch64-linux-gnu-
sudo make ARCH=arm64 modules_install O=../build/ CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=/path/to/rootfs

sudo chroot rootfs

apt-get update
apt-get install sudo vim bash-completion -y
apt-get install net-tools ethtool ifupdown network-manager iputils-ping -y
apt-get install rsyslog resolvconf udev -y

# 如果上面软件包没有安装,但至少要安装下面的包
apt-get install systemd -y

adduser arm64
adduser arm64 sudo
echo "kernel-4_19" >/etc/hostname
echo "127.0.0.1 localhost" >/etc/hosts
echo "127.0.0.1 kernel-4_19">>/etc/hosts
dpkg-reconfigure resolvconf
dpkg-reconfigure tzdata
exit

sudo umount rootfs/proc
sudo umount rootfs/sys
sudo umount rootfs/dev/pts
sudo umount rootfs/dev
sudo umount rootfs

模拟运行验证

1
2
qemu-system-aarch64 -machine virt,gic_version=3 -machine virtualization=true -cpu cortex-a57 -machine type=virt -nographic -smp 4 -m 4096 -kernel build/arch/arm64/boot/Image -append "console=ttyAMA0 root=/dev/vda rw" -drive if=none,file=ubuntu-18.04.1-rootfs_ext4.img,id=hd0,format=raw -device virtio-blk-device,drive=hd0 -netdev tap,id=net0,ifname=tap0,script=no,downscript=no -device virtio-net-device,netdev=net0,mac=52:55:00:d1:55:01

ubuntu-rootfs.jpeg

图3 使用ubuntu-rootfs模拟运行的输出

问题
在使用ubuntu-base启动虚拟aarch64平台时,等待dev-ttyAMA0.device超时而卡住,从而导致无法登录进入bash

hang up on device-ttyAMA0

图4 在使用ubuntu-rootfs模拟运行时挂机问题

解决方法

1
2
3
4
5
sudo mount ubuntu-18.04.1-rootfs_ext4.img rootfs/
sudo chroot rootfs/
cp lib/systemd/system/serial-getty\@.service lib/systemd/system/serial-getty\@ttyAMA0.service
systemctl enable serial-getty\@ttyAMA0.service
exit

修改内容

1
sudo vim rootfs/lib/systemd/system/serial-getty@ttyAMA0.service 

modify

图5 修改的内容

BindsToAfter开头的行注释掉
参考地址:systemd for Administrators, Part XVI

1
sudo umount rootfs

最后卸载挂载,完成根文件系统的制作

busybox

1
2
3
4
5
6
7
wget  https://busybox.net/downloads/busybox-1.30.0.tar.bz2
tar -xjf busybox-1.30.0.tar.bz2
cd busybox-1.30.0
make defconfig
make menuconfig
make -j4
make install

menuconfig.jpeg

图6 执行make menuconfig后修改的配置选项

制作步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dd if=/dev/zero of=busybox-1.30.0-rootfs_ext4.img bs=1M count=100 oflag=direct
mkfs.ext4 busybox-1.30.0-rootfs_ext4.img
mkdir rootfs
sudo mount busybox-1.30.0-rootfs_ext4.img rootfs/
sudo cp -raf busybox-1.30.0/_install/* rootfs/

cd rootfs
sudo mkdir -p proc sys tmp root var mnt dev
sudo mknod dev/tty1 c 4 1
sudo mknod dev/tty2 c 4 2
sudo mknod dev/tty3 c 4 3
sudo mknod dev/tty4 c 4 4
sudo mknod dev/console c 5 1
sudo mknod dev/null c 1 3
sudo cp -r ../busybox-1.30.0/examples/bootfloppy/etc/ .

cd ..
sudo umount rootfs

模拟运行

1
qemu-system-aarch64 -kernel build/arch/arm64/boot/Image -append "console=ttyAMA0 root=/dev/vda init=/linuxrc rw" -m 2048M -smp 4  -M virt -cpu cortex-a57 -nographic -hda busybox-1.30.0-rootfs_ext4.img

busybox-output.jpeg

图7 使用busybox-rootfs模拟运行的输出

由于未作任何系统配置,比如/etc/passwd、/etc/group、/etc/shadow、/etc/hostname等文件,所以系统的操作和易用性还有待改进。

制作initramfs

1
2
3
4
5
6
cd rootfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz

或者
cd build #因为gen_init_cpio在build/usr/目录
sh linux-4.19.27/usr/gen_initramfs_list.sh -o /path/to/initramfs.cpio.gz /path/to/rootfs/

运行实验

1
qemu-system-aarch64 -kernel build/arch/arm64/boot/Image -initrd initramfs.cpio.gz -append "console=ttyAMA0 rdinit=/linuxrc" -M virt -cpu cortex-a53 -nographic -m 2048M

initramfs.jpeg

图8 使用initramfs.cpio.gz模拟运行的输出