qemu arm64 仿真

环境:ubuntu 20.04

准备工作

1
2
$ sudo apt-get install qemu qemu-system-arm gcc-aarch64-linux-gnu gdb-multiarch
$ sudo apt install flex bison libssl-dev

编译内核

下载 Linux 源码:kernel 各版本下载

下载完内核之后,如放在 ~/linux-5.10 目录下:

1
2
3
4
$ cd linux-5.10
$ mkdir out
$ make O=out ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
$ make O=out ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image -j8

制作 rootfs

我使用 busybox 制作 rootfs。这里使用 busybox-1.36.1.tar.bz2 作为演示:

注:如果是编译平台是 Linux 6.8+ 版本内核,需要打上 https://gitweb.gentoo.org/repo/gentoo.git/tree/sys-apps/busybox/files/busybox-1.36.1-kernel-6.8.patch?id=d8ad860a1ed9aa92adaa7dcf1c3fc78d0e2f80ce patch,否则会出现 https://github.com/gramineproject/gramine/issues/1909 问题(error: ‘TCA_CBQ_RATE’ undeclared):

image-20240720145618265

1
2
3
4
5
$ wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
$ tar -xvf busybox-1.36.1.tar.bz2
$ cd busybox-1.36.1/
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

修改:

-> Settings

[*] Build static binary (no shared libs)

menuconfig.png

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
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8 && make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- install
$ make _install
$ cd _install
$ sudo mkdir dev etc mnt
$ sudo mkdir -p etc/init.d/
$ sudo vim etc/init.d/rcS
#!/bin/sh
mkdir /proc
mkdir /tmp
mkdir /sys
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
$ sudo chmod 777 etc/init.d/rcS
$ sudo vim etc/fstab
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
devtmpfs /dev devtmpfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
$ sudo vim etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
$ find . | cpio -o -H newc | gzip > rootfs.cpio.gz

启动内核

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cd ~
$ mkdir arm64
$ cd arm64
$ cp ~/busybox-1.36.1/_install/rootfs.cpio.gz .
$ cp ~/linux-5.10/out/arch/arm64/boot/Image .
$ cat qemu.sh
#!/usr/bin/sh
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=8192M \
-cpu cortex-a72 \
-smp 2 \
-kernel Image \
-initrd rootfs.cpio.gz \
--append "console=ttyAMA0 rdinit=/linuxrc crashkernel=128M"
$ chmod a+x qemu.sh
$ ./qemu.sh

正常启动会输出 kernel log,并按回车键可以到控制台,类似:

qemu.png

qemu 和 host 文件共享

参考 https://tjtech.me/transfer-files-between-qemu-and-host-via-mount.html

有时需要从 qemu 中将文件传到 host 中,或者需要将 host 中的文件传到 qemu 中:

1
2
3
4
$ dd if=/dev/zero of=disk.img bs=1M count=1024
$ mkfs.ext4 disk.img
$ mkdir disk_mnt //建个挂载点
$ sudo mount disk.img disk_mnt/

可以将需要传送到 qemu 的文件放到 disk_mnt 下了,然后 sudo umount disk_mnt

修改 qemu.sh

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/sh
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=8192M \
-cpu cortex-a72 \
-smp 2 \
-kernel Image \
-initrd rootfs.cpio.gz \
-drive file=disk.img,format=raw,if=virtio \
--append "console=ttyAMA0 rdinit=/linuxrc crashkernel=128M"

这个块设备我还没研究过,在我这里启动 qemu 之后的块设备是 /dev/vda

1
2
3
4
5
6
7
/ # fdisk -l
Disk /dev/vda: 1024 MB, 1073741824 bytes, 2097152 sectors
2080 cylinders, 16 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytes

Disk /dev/vda doesn't contain a valid partition table
/ #

那么就可以开机时将 /dev/vda 挂载起来,如修改 etc/init.d/rcS 文件,增加一行:

1
mount -t ext4 /dev/vda /mnt/

启动 qemu 之后可以将需要传送到 host 的文件拷贝到 /mnt 目录下,host 想要查看则需要重新 mount。

内核调试

修改 qemu.sh 脚本,增加 -s -S 参数:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/sh
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-s -S \
-nographic \
-m size=8192M \
-cpu cortex-a72 \
-smp 2 \
-kernel Image \
-initrd rootfs.cpio.gz \
-drive file=disk.img,format=raw,if=virtio \
--append "console=ttyAMA0 rdinit=/linuxrc crashkernel=128M"

-s-gdb tcp::1234 简写。

-S 是在冻结 CPU。

在另外一个终端,运行 gdb 调试:

1
2
3
$ cd ~/arm64
$ cp ~/linux-5.10/out/vmlinux .
$ gdb-multiarch vmlinux -ex "target remote :1234"