QEMU指令添加实现源码解析2023年6月6日 14:531、指令编码–Decode TreeQEMU 中的指令的二进制编码都是以Decode Tree方式进行定义的QEMU 的内部程序在编译的时候会将其自动解析为 C 语言。示例# target/riscv/insn*.decode addw 31:25 ..... 24:20 19:15 14:12 11:7 6:0 // 指令的格式 pattern addw 010000 ..... ..... 000 ..... 1110111 r // 指令的格式 pattern funct7 Rs2 Rs1 funct3 Rd opcode # Formats 32: r ...... ...... ...... ...... ...... r %rs2 %rs1 %rd*.decode 文件分析a. 字段 fields 的表述# Fields: %rs3 27:5 %rs2 20:5 %rs1 15:5 %rd 7:5 %sh10 20:10 %sh10 20:10 %sr 20:12 %rm 12:3说明以%rs3为例从左向右表示占位从第 27 位开始的 5 位。b. 立即数的表述立即数也是一种 field表述方式与上述相似其中s12表示 12 位的有符号数。下面的b,j类型代表了这个立即数的拼接顺序。# immediates: %imm_i 20:s12 %imm_i 20:s12 %imm_s 25:s7 7:5 %imm_b 31:s1 7:1 25:6 8:4 !functionex_shift_1 %imm_j 31:s1 12:8 20:1 21:10 !functionex_shift_1 %imm_u 12:s20 !functionex_shift_12 %imm_j 31:s1 12:8 20:1 21:10 !functionex_shift_12后面的function代表了如何处理这些字段为了获得立即数实现在同目录下的translate.c中。c. Arguments 参数这里记录了不同指令类型的参数即arguments# Arguments: b imm rs2 rs1 i imm rs1 rd r rd rs1 rs2 shift shamt rs1 rd atomic aq rl rs2 rs1 rdd. 指令格式 Format表述了各种不同的指令格式# Formats 32: r ...... ...... ...... ...... ...... ...... ...... r %rs2 %rs1 %rd i ...... ...... ...... ...... ...... ...... ...... i imm%imm_i %rs1 %rd b ...... ...... ...... ...... ...... ...... ...... b imm%imm_b %rs2 %rs1 s ...... ...... ...... ...... ...... ...... ...... s imm%imm_s %rs2 %rs1 u ...... ...... ...... ...... ...... ...... ...... u imm%imm_u %rd j ...... ...... ...... ...... ...... ...... ...... j imm%imm_j %rd j ................................................ j imm%imm_j %rd sh ...... ...... ...... ...... ...... ...... ...... shift shamt%sh10 %rs1 %rd csr ...... ...... ...... ...... ...... ...... ...... %csr %rs1 %rd atom ld ...... aq:1 rl:1 ...... ...... ...... ...... atomic %rs2 %rs1 %rd atom ld ...... aq:1 rl:1 ...... ...... ...... ...... atomic rs20 %rs1 %rd atom st ...... aq:1 rl:1 ...... ...... ...... ...... atomic %rs2 %rs1 %rd r4_rm ...... ...... ...... ...... ...... ...... ...... %rs3 %rs2 %rs1 %rm %rd r_rm ...... ...... ...... ...... ...... ...... ...... %rs2 %rs1 %rm %rd r_rm ...... ...... ...... ...... ...... ...... ...... %rs1 %rm %rd r2_rm ...... ...... ...... ...... ...... ...... ...... %rs1 %rd r2 ...... ...... ...... ...... ...... ...... ...... %rs1 %rd sfence_vma ...... ...... ...... ...... ...... ...... ...... %rs2 %rs1 sfence_vma ...... ...... ...... ...... ...... ...... ...... %rs1 sfence_vm ...... ...... ...... ...... ...... ...... ...... %rs1e. 指令的实现各种指令的实现在以下目录[s29363localhost riscv]$viminsn_trans/trans_rv trans_rva.c trans_rvc.inc.c trans_rvd.inc.c trans_rvf.inc.c trans_rvi.inc.c trans_rvm.inc.c trans_rvt.inc.c编译 QEMU 源码时会在build目录生成相关的解码过程调用到上述文件。2、指令定义过程指令定义有以下几个过程根据指令的定义将指令名称和二进制编码填入根据指令对于参数的定义选取合适的 Format例如r就顺序包含了两个输入寄存器 rs1 rs2 和输出寄存器 rd。注若是新定义的指令类型需要在 QEMU 中添加对应的 Format若没有需要自己去定义3、指令转译QEMU 在执行时会将target instructions这里是 RISC-V instructions转译成TCG ops而 TCG ops 则转译成host instructionse.g. x86 instruction。而trans_xx()函数实际执行了对应该指令的 TCG ops。#! QEMU dynamic instructions translation ---------------------------------------------- | Target Instructions | -- | TCG ops | -- | Host instructions | | (e.g. RISC-V) | (e.g. x86) | ----------------------------------------------一般情况下为了方便定义和区分新建一个文件trans_rvt.inc.c用来定义上述需要实现的trans_xx()函数。[s29363localhost riscv]$lsinsn_trans/ trans_privileged.inc.c trans_rva.inc.c trans_rvc.inc.c trans_rvd.inc.c trans_rvf.inc.c trans_rvi.inc.c trans_rvm.inc.c trans_rvt.inc.c4、指令转译函数示例例trans_add16函数/* * RISC-V translation routines for the RVP Standard Extension. */staticbooltrans_add16(DisasContext*ctx,arg_pcnt*a){if(a-rd!0){TCGv t0tcg_temp_new();TCGv t1tcg_temp_new();TCGv rttcg_temp_new();gen_get_gpr(t0,a-rs1);gen_get_gpr(t1,a-rs2);gen_helper_add16(rt,t0,t1);gen_set_gpr(a-rd,rt);tcg_temp_free(rt);tcg_temp_free(t0);tcg_temp_free(t1);}returntrue;}注意事项在 RISC-V 中x0zero register的写入都会被忽略所以首先要判断rd是否为 0若为 0 则不做任何事情。接下来需要声明 TCG 的变量t0,t1和rt利用gen_get_gpr()将rs1,rs2寄存器的值存入变量声明一个新变量rt利用gen_set_gpr()将结果存入rd寄存器中。利用新声明的变量调用gen_helper_add16()函数转向 helper function该函数计算完成后会将结果保存在rd即cpu_gpr[a-rd]寄存器中。5、指令的逻辑–helper function在target/riscv/helper.h中定义添加指令的宏#defineDEF_HELPER_2(name,t1,t2)\DEF_HELPER_FLAGS_2(name,0,ret,t1,t2)// DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2)为了方便 QEMU 对于 helper function 的调用和定义我们需要定义一个函数DEF_HELPER_x 不带FLAGS的DEF_HELPER_FLAGS_x对 QEMU 声明函数的名称和参数x代表该指令需要的参数自变量不带FLAGS的函数会利用命令自动将FLAGS参数置为 0。参数说明name指令的名称连接成HELPER(name)/helper_name的形式作为 helper functionflag函数权限位TCG 调用的权限全局不读/写返回值无用无返回值。tcg.hrethelper function 返回值t1 - tnhelper function 的参数参数类型说明类型意义tltarget_ulong- QEMU 中保存寄存器值的基本单位envenvironment-CPUXXSTATE保存 CPU 状态寄存器的值i64integer-64- 64 位整型可用于浮点数指令add16 的 helper function 实现在helper.h中声明DEF_HELPER_2(add16,tl,tl,tl)DEF_HELPER_FLAGS_3(add16,0,tl,tl,tl)在bitmanip_helper.c中实现/* * RISC-V P Extension Helpers for QEMU. */#includeqemu/osdep.h#includecpu.h#includeexec/exec-all.h#includeexec/helper-proto.h#defineu16puint16_t*#ifdefined(TARGET_RISCV32)constuint32_tLC_16BIT2;#elseconstuint32_tLC_16BIT4;#endiftarget_ulonghelper_add16(target_ulong rs1,target_ulong rs2){target_ulong rd0;u16p rs1_p(u16p)rs1;u16p rs2_p(u16p)rs2;u16p rd_p(u16p)rd;for(unsignedi0;iLC_16BIT;i){rd_p[i]rs1_p[i]rs2_p[i];}returnrd;}QEMU测试程序2023年6月30日 16:081、汇编入口汇编# See LICENSE for license details. .section .text.init,ax,progbits .section .text.init,ax,progbits .globl _start _start: csrr t0, mhartid # read hardware thread id (hart stands for hardware thread), 将线程id放到t0寄存器 bnez t0, halt # run only on the first hardware thread (hartid 0), halt all the other threads. 判断t0值是否为0为0则向下执行否则跳转到halt la sp, stack_top # setup stack pointer //将栈顶地址加载到sp寄存器中 j main #跳转到main标签即执行主程序 j main #跳转到main标签即执行主程序 halt: j halt # enter the infinite loop2、主程序staticinta3;staticintb3;staticintb256;staticvolatileint*uart(int*)0x10013000;staticintcustom48(longaddr,longaddr1){intret0;asmvolatile(jobget48 %[dest],%[src1],%[src2]:[dest]r(ret):[src1]r(addr),[src2]r(addr1));returnret;}staticintcustom64(longaddr,longaddr1){intret0;asmvolatile(jobget64 %[dest],%[src1],%[src2]:[dest]r(ret):[src1]r(addr),[src2]r(addr1));returnret;}voidmain(){intret10;intret20;ret1custom48((long)a,(long)b);ret2custom64((long)a,(long)b);if(ret1){uart[0]o;uart[0]k;uart[0]4;}if(ret2){uart[0]o;uart[0]k;uart[0]4;}else{uart[0]e;uart[0]r;uart[0]r;}while(1);}3、Makefile文件PREFIX /home/s29363/riscv/install-64/bin/riscv64-unknown-elf- CC $(PREFIX)gcc LD $(PREFIX)ld OBJDUMP /home/s29363/qemu/qemu-system/install-64/bin/qemu-system-riscv64 CFLAGS -static -mcmodelmedany -fvisibilityhidden -fno-function-sections -fdata-sections -nostdlib -nostartfiles -nodefaultlibs -g -O0 LDFLAGS -T /home/s29363/qemu/qemu-system/tests/tcg/riscv64/default.lds -nmagic -gc-sections QEMU_FLAGS -nographic -machine sifive_u -bios none -kernel TARGET hello $(TARGET):start.o hello.o $(LD) $(LDFLAGS) start.o hello.o -o $(TARGET) start.o: start.s $(CC) $(CFLAGS) start.s -o start.o hello.o: hello.c $(CC) $(CFLAGS) hello.c -o hello.o .PHONY: qemu qemu: $(QEMU) $(QEMU_FLAGS) hello .PHONY: dis dis:hello $(OBJDUMP) -d hello.o .PHONY: clean clean: rm -rf $(TARGET) start.o hello.o