2. QEMU RISC-V 环境搭建:从源码到第一个虚拟机
环境搭建这事儿,说简单也简单,说坑也多。我见过不少同事卡在编译环节,一卡就是半天。其实只要理清几个关键点,整个过程挺顺畅的。这一章咱们就把 QEMU RISC-V 的环境从头搭一遍,从源码编译到启动第一个虚拟机,一步不落。
2.1 源码编译与安装
我个人习惯用源码编译,而不是直接装包管理器里的版本。为什么?因为调试的时候需要打开一些额外的编译选项,比如 --enable-debug,包管理器给的二进制通常不带这些。
2.1.1 获取源码
git clone https://gitlab.com/qemu-project/qemu.git
cd qemu
git checkout stable-8.2 # 我建议用稳定分支,别追 master
我曾经追过一次 master,结果某个提交把 RISC-V 的 PMP 逻辑改坏了,排查了两天才发现是上游的临时问题。所以,除非你要给 QEMU 提 patch,否则老老实实用 stable。
2.1.2 配置与编译
QEMU 支持两种模式:系统模式(system mode)和用户模式(user mode)。系统模式模拟完整机器,能跑操作系统;用户模式只模拟用户态指令,适合跑单个程序。咱们做性能分析和调试,两种都得编译。
mkdir build && cd build
# 配置系统模式 + 用户模式,目标架构只选 riscv64
../configure \
--target-list=riscv64-softmmu,riscv64-linux-user \
--enable-debug \
--enable-kvm \
--enable-virtfs \
--prefix=/opt/qemu-riscv
make -j$(nproc)
make install
--enable-debug 会打开 QEMU 内部的断言和日志,性能会降一点,但调试时能省大量时间。生产环境可以去掉这个选项。
编译时间取决于机器。我的一台 8 核笔记本,大概 15 分钟能编完。如果遇到依赖缺失,比如 glib2、pixman、libfdt,用包管理器装上就行。
2.1.3 验证安装
/opt/qemu-riscv/bin/qemu-system-riscv64 --version
/opt/qemu-riscv/bin/qemu-riscv64 --version
能看到版本号,说明编译安装成功了。嗯,这里要注意:qemu-system-riscv64 是系统模式,qemu-riscv64 是用户模式,别搞混。
2.2 系统模式与用户模式配置
这两种模式的区别,说白了就是「模拟一台电脑」和「模拟一个程序」。我画了张图,帮你快速理解它们的定位。
你想想看,如果你只是想跑一个 RISC-V 的 Hello World,用用户模式就够了,几毫秒就出结果。但如果你要调试 Linux 内核的启动流程,那就必须用系统模式。
2.3 常用启动参数详解
QEMU 的参数非常多,但常用的也就十几个。我把它们分成三类,列了个表格,方便你查阅。
| 类别 | 参数 | 说明 | 示例 |
|---|---|---|---|
| 机器配置 | -machine |
指定机器类型,RISC-V 常用 virt |
-machine virt |
-cpu |
CPU 型号,可选 rv64、sifive-u54 等 |
-cpu rv64 |
|
-smp |
模拟的 CPU 核心数 | -smp 4 |
|
| 内存与存储 | -m |
内存大小,支持单位 G/M | -m 2G |
-kernel |
指定内核镜像文件 | -kernel vmlinux |
|
-drive |
挂载磁盘或 rootfs 镜像 | -drive file=rootfs.ext4,format=raw |
|
| 调试与监控 | -s |
等价于 -gdb tcp::1234,开启 GDB 调试端口 |
-s |
-S |
启动时暂停 CPU,等待 GDB 连接 | -S |
|
-d |
打开 QEMU 内部日志,如 in_asm、cpu、int |
-d in_asm,cpu |
-d 参数里同时开了 in_asm 和 int,结果日志刷屏,虚拟机直接卡死。建议调试时只开需要的日志类别,别贪多。
2.4 第一个 RISC-V 虚拟机启动
理论说完了,咱们动手。启动一个 RISC-V 虚拟机需要三样东西:OpenSBI(机器模式固件)、Linux 内核、rootfs(根文件系统)。
2.4.1 准备启动文件
我建议直接用 Buildroot 生成这三样,省心。当然你也可以从发行版拿现成的,比如 Fedora RISC-V 的镜像。
# 用 Buildroot 快速生成(我常用的方式)
git clone https://git.buildroot.net/buildroot
cd buildroot
make qemu_riscv64_virt_defconfig
make -j$(nproc)
# 生成的文件在 output/images/ 下:
# fw_jump.elf -> OpenSBI
# Image -> Linux 内核
# rootfs.ext2 -> 根文件系统
2.4.2 启动命令
/opt/qemu-riscv/bin/qemu-system-riscv64 \
-machine virt \
-cpu rv64 \
-smp 2 \
-m 2G \
-kernel output/images/fw_jump.elf \
-device loader,file=output/images/Image,addr=0x80200000 \
-drive file=output/images/rootfs.ext2,format=raw,if=virtio \
-append "root=/dev/vda rw console=ttyS0" \
-nographic
解释一下关键点:
-kernel加载的是 OpenSBI,不是 Linux 内核。OpenSBI 会跳转到0x80200000去执行内核。-device loader把内核镜像放到指定内存地址。-append传递内核启动参数,console=ttyS0让内核把日志输出到串口。-nographic把串口重定向到当前终端,不需要图形界面。
2.4.3 启动效果
执行上面的命令后,你会看到 OpenSBI 的启动信息,然后是 Linux 内核的启动日志,最后出现登录提示符。默认用户名 root,无密码。
OpenSBI v1.3
____ _____ ____ _____
/ __ \| _ \ ___|/ ____|
| | | | |_) | | | (___
| | | | _ <| | \___ \
| |__| | |_) | |___ ____) |
\____/|____/\___|_____/
Platform Name : riscv-virtio
Platform Features : medeleg
...
[ 0.000000] Linux version 6.6.0 ...
[ 0.123456] Built 1 zonelists, mobility grouping on.
...
Welcome to Buildroot
buildroot login: root
#
嗯,这里有个小细节:如果你发现启动后卡在 console=ttyS0 没输出,八成是内核编译时没打开串口驱动。检查内核配置里的 CONFIG_SERIAL_8250 和 CONFIG_SERIAL_OF_PLATFORM 是否开启。
我个人习惯把这条启动命令存成一个脚本,比如 start-vm.sh,每次调试直接运行,不用重复敲参数。你也可以把参数写进 qemu-system-riscv64 的配置文件里,用 -readconfig 加载,更整洁。