本文是基于格维开源社区QEMU训练营-CPU建模而来,原文

QEMU CPU 建模流程:以 RISC-V 为例 - QEMU Camp Tutorial

在原文中,我们需要手动定义 CPUDef 结构体,手动编写 class_init 函数,再手动将其注册到类型数组中。但在较新的 QEMU 版本中,官方提供了极其强大的宏(Macro)。预处理器会在编译时自动帮我们展开这些结构体,这让 CPU 建模变得异常简单。

首先定义CPU名称,打开target/riscv/cpu-qom.h,添加:

1
#define TYPE_RISCV_CPU_G233       RISCV_CPU_TYPE_NAME("g233-cpu")

RISCV_CPU_TYPE_NAME 这个宏会在编译时将 "g233" 展开为 "g233-riscv-cpu",这是 QEMU 内部对象树识别该硬件的唯一字符串凭证。

然后使用宏注册CPU蓝图

在cpu.c里找到static const TypeInfo riscv_cpu_type_infos[],插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#if defined(TARGET_RISCV64)
/* 其他官方 CPU 定义... */

/* 添加我们的自定义核心 G233 */
DEFINE_RISCV_CPU(TYPE_RISCV_CPU_G233, TYPE_RISCV_VENDOR_CPU,
.misa_mxl_max = MXL_RV64, /* 支持 64 位架构 */
.misa_ext = RVI | RVM | RVA | RVC | RVU, /* 开启的基础指令集扩展 */
.priv_spec = PRIV_VERSION_1_12_0, /* 特权级规范版本 */
.cfg.ext_zicsr = true, /* 开启 CSR 寄存器支持 */
.cfg.ext_zifencei = true, /* 开启指令栅栏 */
.cfg.mmu = true, /* 开启内存管理单元 */
.cfg.pmp = true, /* 开启物理内存保护 */
.cfg.max_satp_mode = VM_1_10_SV39, /* 虚拟内存分页模式 */
),
#endif /* TARGET_RISCV64 */

在这里,我们让 g233 继承自 TYPE_RISCV_VENDOR_CPU。传入的每一个 .cfg 参数,都会被宏自动打包成静态结构体,并隐式生成 class_init 挂载函数。切记不要再去手动写 class_init,否则会引发重定义报错!

然后可以添加指令集扩展

指令集扩展通过 RISCVCPUConfig 结构体来配置,每个扩展对应一个 bool 类型的成员变量。

这个结构体在 target/riscv/cpu_cfg.h 中定义。

1
2
3
4
5
struct RISCVCPUConfig {
#define BOOL_FIELD(x) bool x;
#define TYPED_FIELD(type, x, default) type x;
#include "cpu_cfg_fields.h.inc"
};

可以看到官方对这部分进行了重构。为了让所有扩展标志位能够统一参与序列化和合并计算,官方引入了 cpu_cfg_fields.h.inc 这个文件。

打开 target/riscv/cpu_cfg_fields.h.inc 文件,在里面加一行:

1
BOOL_FIELD(ext_g233)

接下来编译,在终端回到build文件夹,

1
make -j$(nproc)

然后坑来了,原文直接执行

1
qemu-system-riscv64 -cpu help

结果根本没有g233,因为当我们在终端直接输入 qemu-system-riscv64 时,Linux 会去系统的全局环境变量(如 /usr/bin/)中寻找可执行文件。此时调用的其实是系统自带的、旧版本的 QEMU,而不是新生成的核心。

实际上该执行(在build目录里)

1
./qemu-system-riscv64 -cpu help

输出如下:

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
huzayn@huzayn-VMware-Virtual-Platform:~/qemu/build$ ./qemu-system-riscv64 -cpu help
Available CPUs:
g233-cpu
lowrisc-ibex
max
max32
mips-p8700
rv32
rv32e
rv32i
rv64
rv64e
rv64i
rva22s64
rva22u64
rva23s64
rva23u64
shakti-c
sifive-e31
sifive-e34
sifive-e51
sifive-u34
sifive-u54
thead-c906
tt-ascalon
veyron-v1
x-rv128
xiangshan-kunminghu
xiangshan-nanhu