DecodeTree机制
DecodeTree是QEMU在17年引入的机制,在给riscv板卡实现新指令的时候引入的。
DecodeTree简化了指令格式的定义以及指令解析函数的实现。通过在.decode文件里面使用特定格式定义指令和指令格式,decodetree机制会通过脚本自动生成对应的解析函数。
DecodeTree的详细格式
Link,可以当manual,编程的时候查看:
- 汉化文档: https://gevico.github.io/learning-qemu-docs/ch2/sec2/qemu-accel-tcg/tcg-add-guest-insn/
- 全英官方: https://www.qemu.org/docs/master/devel/decodetree.html
- 汉化文档(内容和第一个差不多吧): https://lifeislife.cn/posts/qemu-decodetree详解/
例如添加这个指令,符合R-Type格式(应该是RISC-V里面的一种通用的指令格式)
31 25 24 20 19 15 14 12 11 7 6 0
+---------+--------+--------+----------+-------------------+-------+
| func7 | rs2 | rs1 | funct3 | rd | opcode| R-type
+---------+--------+--------+----------+-------------------+-------+6 6 0x7b
+---------+--------+--------+----------+-------------------+-------+
| 0000110 | rs2 | rs1 | 110 | rd |1111011| dma
+---------+--------+--------+----------+-------------------+-------+
在insn32.decode添加指令信息的定义,我们直接复用R-type的指令解析定义@r
dma 0000110 ..... ..... 110 ..... 1111011 @r
下面的内容是insn32.decode里面已经有的,不用自己添加
@r的定义如下
# Formats 32:
@r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd
意思是从32位指令里提取出rs2 rs1 和 rd
然后rs2 rs1和rd的字段定义以及r-type的format sets定义如下:
# Format sets
&r rd rs1 rs2# Fields
%rs2 20:5
%rs1 15:5
%rd 7:5
rs2表示从20位开始,大小为5位。其他的类似意思。
实现指令逻辑
指令的逻辑实现在后缀为.c.inc的文件中
可以仿照其他的.c.inc文件编写指令的逻辑
在.c.inc文件里面使用定义的helper函数,在定义helper函数的宏指定的函数名前面加一个gen_helper_前缀。
比如定义
DEF_HELPER_4(dma, void, env, tl, tl, tl)
那么使用这个dma helper函数需要调用gen_helper_dma(...)
.c.inc文件的语法好像和C是一致的
例如:
static bool trans_dma(DisasContext *ctx, arg_dma *a)
{TCGv dst = get_gpr(ctx, a->rd, EXT_NONE);TCGv src = get_gpr(ctx, a->rs1, EXT_NONE);TCGv gran = get_gpr(ctx, a->rs2, EXT_NONE);gen_helper_dma(tcg_env, dst, src, gran);return true;
}
helper函数
指令实现需要使用到的helper函数在xxx_helper.c里面实现,在helper.h里面声明这些helper函数,使用QEMU提供的一类宏DEF_HELPER_[n]()。这个n是参数的数量。宏的第一个参数是helper的名字,第二个参数是函数返回值类型,之后的参数是函数的参数类型。
宏定义出来的函数名是第一个参数加上前缀helper_
如果helper函数里面需要传入State,那么用env标记。需要传入整型,使用i32或者使用tl表示目标架构的机器位宽。使用ptr表示传入一个void*指针
如果新增了xxx_helper.c,需要在QEMU的构建系统里面添加这个新的文件。QEMU使用meson构建,在同级目录下面的meson.build里面仿照其他文件添加的形式添加新文件。
在xxx_helper.c里面实现helper函数需要使用HELPER(name)宏来定义函数签名
例如
// helper.h
DEF_HELPER_4(dma, void, env, tl, tl, tl)// xxx_helper.c
void HELPER(dma)(CPURISCVState *env, target_ulong dst, target_ulong src, target_ulong gran)
{int size = 8 << gran;for (int i = 0; i < size; i++) {for (int j = 0; j < size; j++) {uint32_t val = cpu_ldl_data(env, src + (i * size + j) * sizeof(uint32_t));cpu_stl_data(env, dst + (j * size + i) * sizeof(uint32_t), (uint32_t)val);}}
}
TCG API
TCG提供了一系列抽象的API来操作寄存器,将不同架构下的一些操作统一了,便于实现自定义指令,而且比用自己写的helper速度更快。
TCG提供了很多类型的操作,比如读取写入和一些数学计算。还提供了一些有关创建寄存器、创建临时寄存器、标签的操作。
TCG API 链接, 同样可以作为manual:
- QEMU源码的
tcg/tcg_op.h文件,具有所有tcg_op的声明 - QEMU wiki对tcg_ops的文字描述: https://wiki.qemu.org/Documentation/TCG/frontend-ops
