JasonWang's Blog

如何通过QEMU启动Linux系统

字数统计: 1.3k阅读时长: 5 min
2022/08/27

看Linux驱动相关的代码, 却没有一个好的调试环境可以跟踪内核相关的调用流程。于是,想着用QEMU虚拟机来搭建一个Linux系统。花了大半天时间,终于能够启动一个简单的Linux系统了。中间踩了不少坑,找了不少资料,这里简单总结下整个过程。

以下操作都是基于Ubuntu 18.04 x86_64平台

最开始参考了如何使用QEMU跑内核,使用系统自带的QEMU工具,结果提示如下错误:

1
2
3

qemu-system-aarch64 rom check and register reset failed

怀疑是QEMU的版本太低导致,于是只好又重新编译QEMU源码,最后总算大功告成。总的说来,大致要做的事情有这么几个:

  • 编译Linux内核,利用busybox来生成一个小的rootfs(关于什么是rootfs,可以参考The rootfs FS)
  • 编译QEMU,确保正常配置ARM64架构的虚拟环境
  • 一切就绪,通过qemu-system-aarch64跑起来虚拟机来

编译Linux内核

交叉编译Linux内核,需要确保系统中存在必须的编译环境与工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

sudo apt-get install build-essential
sudo apt-get install binutils-multiarch
sudo apt-get install ncurses-dev

sudo apt-get install gcc-aarch64-linux-gnu

```

拷贝源码`git clone https://github.com/torvalds/linux`, 进入对应目录, 执行:

```bash
#创建编译目录,用于输出产物
mkdir build

make distclean
make O=./build ARCH=arm64 defconfig

#如果需要编辑某些config,可以使用menuconfig来配置
make O=./build ARCH=arm64 nconfig

#编译
make O=./build ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8

编译成功后,会在arch/arm64/boot下面生成内核镜像:

1
2
3

-rw-rw-r-- 1 jason jason 36M Aug 25 14:20 arch/arm64/boot/Image

这个就是后面用于跑Linux内核的镜像文件。

编译Qemu源码

编译QEMU比较方便,先下载源码:

1
2
3
4
5
6
7
8
9
10
11
12

git clone https://github.com/qemu/qemu

cd qemu

mkdir build
cd build
../configure
make -j16

make install

编译大概会持续十几分钟的样子,最后记得编译完成后执行make install, 否则在最后启动虚拟机时会提示:

1
2
3

qemu-system-aarch64: failed to find romfile "efi-virtio.rom"

编译Busybox,制作rootfs

编译Busybox稍微麻烦一点,需要编辑配置列表,确保生成的是静态库形式的执行文件, 大概有这么几个步骤:

1
2
3
4
5
6
7
8

mkdir build

make O=../build/ ARCH=arm64 defconfig

#通过菜单配置config
make O=../build/ ARCH=arm64 menuconfig

busybox menuconfig

菜单配置的方式与Linux内核的配置类似,按下/可以直接搜索你要设置的选项; 如果觉得菜单配置比较麻烦,也可以通过直接编辑生成的.config来配置选项编译为静态的方式:

1
2
3

CONFIG_STATIC=y

完成配置后,执行编译:

1
2
3
4

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j16
make install

busybox/_install下面可以看到编译的产物。有了busybox我们就可以制作一个简单的rootfs文件系统了。对于busybox的系统来说,大致的启动顺序如下:

1
2
3

/sbin/init --> /etc/inittab --> /etc/init.d/rcS --> /etc/fstab

进入busybox/_install目录可以看到当前生成的目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

4.0K ./proc
1.9M ./bin
16K ./etc
4.0K ./tmp
4.0K ./root
4.0K ./sys
4.0K ./home
4.0K ./var
4.0K ./lib
4.0K ./mnt
12K ./usr
4.0K ./sbin
12K ./dev
34M ./


制作一个rootfs,可以按照如下几个步骤操作:

  • 创建目录
1
2
3
4

# 创建必要的目录
mkdir -p dev etc home lib mnt proc root sys tmp var

  • 生成启动所需的文件

    • etc/inittab: 将如下内容写入到该文件
    1
    2
    3
    4
    5
    6

    ::sysinit:/etc/init.d/rcS
    ::respawn:-/bin/sh
    ::askfirst:-/bin/sh
    ::cttlaltdel:/bin/umount -a -r

    编辑完成后,修改文件权限: chmod 0755 etc/inittab

    • etc/init.d: 系统初始化脚本,可以用于创建设备以及执行文件系统挂载等操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    mkdir -p etc/init.d/

    #编辑rcS文件,输入如下内容

    #!/bin/sh

    echo "mount pseudo sys"

    mount -t proc none /proc
    mount -t sysfs none /sys
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    /sbin/mdev -s

    echo "init rc done"

    为了确保该文件正常执行,需要修改权限chmod 0755 etc/init.d/rcS. 如果除了上述proc/sys文件系统外,还有其他需要挂载的设备,可以放在etc/fstab中:

    1
    2
    3
    4
    5

    temps/tmp rpoc defaults 0 0
    none /tmp ramfs defaults 0 0
    mdev /dev ramfs defaults 0 0

    • 创建特殊文件节点
    1
    2
    3
    4
    5

    cd dev
    sudo mknod console c 5 1
    sudo mknod null c 1 3

    • 将该目录的文件打包成rootfs压缩文件
    1
    2
    3
    4

    find . | cpio -o -H newc > rootfs.cpio
    gzip -c rootfs.cpio > rootfs.cpio.gz

到这里,所有的准备工作就绪了,就下来可以测试下虚拟机是否正常能跑起来了.

QEMU跑起来

执行如下命令,启动Linux虚拟机(具体的参数可以通过qemu-system-aarch64 -h查看)

1
2
3
4
5
6
7
8
9
10

qemu-system-aarch64 \
-machine virt \
-cpu cortex-a57 \
-nographic -smp 1 \
-kernel ./Image \
-initrd ./rootfs.cpio.gz \
-append "rdinit=/linuxrc console=ttyAMA0" \
-s # 开启GDB TCP调试口

正常启动的话,可以看到内核加载的日志以及一个虚拟的终端设备:

Qemu Virt linux

有了这个虚拟环境,无论是驱动开发,还是学习Linux内核都会方便很多了。

参考资料

CATALOG
  1. 1. 编译Linux内核
  2. 2. 编译Qemu源码
  3. 3. 编译Busybox,制作rootfs
  4. 4. QEMU跑起来
  5. 5. 参考资料