搭建NEMU与QEMU的DiffTest环境(Socket方式)

news2024/9/21 0:43:16

搭建NEMU与QEMU的DiffTest环境(Socket方式)

  • 1 简述
  • 2 编译NEMU
    • 2.1 配置
    • 2.2 修改NEMU/scripts/build.mk
    • 2.3 修改isa_difftest_checkregs函数
    • 2.4 修改isa_pmp_check_permission函数
    • 2.5 编译
  • 3 编译qemu-socket-difftest
    • 3.1 修改NEMU/scripts/isa.mk
    • 3.2 修改NEMU/scripts/build.mk
    • 3.3 让qemu-socket-difftest带调试信息
    • 3.4 解决DIFFTEST_REG_SIZE未定义
    • 3.5 修改gdb_getregs函数
    • 3.6 修改difftest_regcpy函数
    • 3.7 让QEMU输出指令日志
    • 3.8 编译生成riscv64-qemu-so
  • 4 以DiffTest方式,运行NEMU
    • 4.1 DiffTest检测到结果相同
    • 4.2 DiffTest检测到结果不同

本文属于 《RISC-V指令集差分测试(DiffTest)系列教程》之一,欢迎查看其它文章。

环境:Ubuntu 18.04.5 LTS

1 简述

本文中,我们选择:

  • QEMU作为参考对象(REF)
  • NEMU作为测试对象(DUT)

2 编译NEMU

2.1 配置

执行以下命令:

apt install build-essential man gcc gdb git libreadline-dev libsdl2-dev zstd libzstd-dev
git clone https://github.com/OpenXiangShan/NEMU.git
cd NEMU/
export NEMU_HOME=/home/test/NEMU    # /home/test/NEMU换成自己NEMU源码目录
make riscv64-benos_defconfig	    # riscv64-benos_defconfig需放入NEMU/configs/
make menuconfig

配置以下内容(让NEMU附带调试信息):

Build Options -> Optimization Level:选择O0,表示编译器不优化
Build Options -> Enable link-time optimization:取消选中,表示禁用链接时优化
Build Options -> Enable debug information:选中,表示使能调试信息

配置以下内容(开启socket方式的DiffTest功能):

Testing and Debugging -> [*] Enable differential testing -> Reference design (QEMU, communicate with socket)

配置以下内容(打开指令跟踪日志):

Testing and Debugging -> [*] Enable debug features: instruction tracing and watchpoint
Testing and Debugging -> [*] Enable rich Log for tracing instructions, pc, inst and dasm

2.2 修改NEMU/scripts/build.mk

在NEMU/scripts/build.mk中,删除所有-Werror选项(否则编译报错),如下所示:

CFLAGS  := -O2 -MMD -Wall -Werror $(INCLUDES) $(CFLAGS)
CXXFLAGS  := -O2 -MMD -Wall -Werror --std=c++17 $(XINCLUDES) $(CFLAGS)

2.3 修改isa_difftest_checkregs函数

在NEMU/src/isa/riscv64/difftest/dut.c中,将isa_difftest_checkregs函数,修改为只检查gpr[32]和PC寄存器,其他不检查。

如下所示:

bool isa_difftest_checkregs(CPU_state *ref_r, vaddr_t pc) {
  csr_prepare();
#ifdef CONFIG_DIFFTEST_REF_SPIKE
  cpu.mip &= 0xffffff4f; // ignore difftest for mip
#endif
  if(cpu.mip != ref_r->mip) ref_r->mip = cpu.mip; // ignore difftest for mip
  if (memcmp(&cpu.gpr[1], &ref_r->gpr[1], ARRLEN(cpu.gpr) - sizeof(cpu.gpr[0])) || (cpu.pc != ref_r->pc)) {
    int i;
    // do not check $0
    for (i = 1; i < ARRLEN(cpu.gpr); i ++) {         // check gpr[20]
      difftest_check_reg(reg_name(i, 4), pc, ref_r->gpr[i]._64, cpu.gpr[i]._64);
    }
    difftest_check_reg("pc", pc, ref_r->pc, cpu.pc); // check pc
    return false;
  }
#ifdef CONFIG_DIFFTEST_STORE_COMMIT
  return difftest_check_store(pc);
#else
  return true;
#endif
}

备注:

isa_difftest_checkregs函数,会将DUT和REF的寄存器进行比较。
原来代码中,编码预期:

  • 如果配置了CONFIG_RVV的话,那么就会进行v0~31寄存器的比较。
  • 如果配置了CONFIG_FPU_SOFT的话,那么就会进行f0~31寄存器的比较。

但是,目前NEMU调用gdb_getregs函数,只能从QEMU获取到gpr[20]和pc寄存器值,因此我们需要这样修改。

2.4 修改isa_pmp_check_permission函数

在NEMU/src/isa/riscv64/system/mmu.c中,修改isa_pmp_check_permission函数,为如下内容:

bool isa_pmp_check_permission(paddr_t addr, int len, int type, int out_mode) {
	return true;
}

备注:

在原来代码中,此函数会返回false,报错。
在配置选项中,未开启DiffTest功能时,运行固件,此函数是直接返回true,所以这里也这么修改。

2.5 编译

编译

make -j`nproc`

编译成功,生成NEMU/build/riscv64-nemu-interpreter可执行文件。

3 编译qemu-socket-difftest

3.1 修改NEMU/scripts/isa.mk

在NEMU/scripts/isa.mk中,将ISA ?= x86修改为ISA ?= riscv64,如下所示:

ISA ?= riscv64
ISAS = $(shell ls $(NEMU_HOME)/src/isa/)
ifeq ($(filter $(ISAS), $(ISA)), ) # ISA must be valid
$(error Invalid ISA=$(ISA). Supported: $(ISAS))
endif
NAME := $(ISA)-$(NAME)
CFLAGS += -D__ISA_$(ISA)__=1

3.2 修改NEMU/scripts/build.mk

在NEMU/scripts/build.mk中,在LDFLAGS末尾,添加-ldl

.DEFAULT_GOAL = app

ifdef SHARE
SO = -so
CFLAGS  += -fPIC -D_SHARE=1
LDFLAGS += -rdynamic -shared -fPIC -Wl,--no-undefined -lz -ldl
endif

注意:build.mk中有多处LDFLAGS,-ldl只能加在上文所示位置。

3.3 让qemu-socket-difftest带调试信息

在NEMU/scripts/build.mk中,将CFLAGS、CXXFLAGS、LDFLAGS中:

  • 所有-O2改为-O0
  • 并且在-O0后,增加-g

如下所示:

CFLAGS  := -O0 -g -MMD -Wall $(INCLUDES) $(CFLAGS)
CXXFLAGS  := -O0 -g -MMD -Wall --std=c++17 $(XINCLUDES) $(CFLAGS)
LDFLAGS := -O0 -g $(LDFLAGS)

3.4 解决DIFFTEST_REG_SIZE未定义

在NEMU/tools/qemu-socket-difftest/include/common.h中,添加#include <difftest.h>
在NEMU/tools/qemu-socket-difftest/src/diff-test.c中,删除#include <difftest.h>

3.5 修改gdb_getregs函数

在NEMU/tools/qemu-socket-difftest/include/isa/riscv64.h中,删除uint64_t fpr[32];,如下所示:

union isa_gdb_regs {
  struct {
    uint64_t gpr[32];
    uint64_t pc;
  };
  struct {
    uint32_t array[DIFFTEST_REG_SIZE/sizeof(uint32_t)];
  };
};

在NEMU/tools/qemu-socket-difftest/src/gdb-host.c中,将gdb_getregs函数,修改为如下内容:

bool gdb_getregs(union isa_gdb_regs *r) { 
  gdb_send(conn, (const uint8_t *)"g", 1);
  size_t size;
  uint8_t *reply = gdb_recv(conn, &size);

  int i;
  uint8_t *p = reply;
  uint8_t c;
  assert(size % (sizeof(uint64_t) * 2) == 0);
  assert((size / 2) <= sizeof(union isa_gdb_regs));
  for (i = 0; i < size / (sizeof(uint64_t) * 2); i ++) {
    c = p[16];
    p[16] = '\0';
    ((uint64_t*)(r->array))[i] = gdb_decode_hex_str(p);
    p[16] = c;
    p += 16;
  }
 
  free(reply);

  return true; 
}

备注:

NEMU可以调用gdb_getregs函数,从QEMU获取到寄存器数据,一共528字节,每个字节为"0" ~ “9"或"a” ~ “f"之间的任一字符,每16个字节表示一个寄存器值,共表示33个寄存器。
比如:
数据包中"1234567823456789…”,那么"1234567823456789",低位在前,高位在后,即表示寄存器值为0x8967452378563412,相当于数据包中用2字节表示1字节的实际数据。


528字节的数据包中,从前往后依次,表示32个gpr与pc寄存器的值。
在这里插入图片描述

3.6 修改difftest_regcpy函数

在NEMU/tools/qemu-socket-diff/src/diff-test.c中,将difftest_regcpy函数,修改为如下内容:

void difftest_regcpy(void *dut, bool direction) {
  union isa_gdb_regs qemu_r;
  gdb_getregs(&qemu_r);
  int dut_pc_off = 32 * sizeof(uint64_t) * 2 + 18 * sizeof(uint64_t);
  if (direction == DIFFTEST_TO_REF) {
    memcpy(qemu_r.gpr, dut/*dut.gpr*/, (32 * sizeof(uint64_t)));
    memcpy(&(qemu_r.pc), (dut + dut_pc_off)/*dut.pc*/, sizeof(uint64_t));
    gdb_setregs(&qemu_r);
  } else {
    memcpy(dut/*dut.gpr*/, qemu_r.gpr, (32 * sizeof(uint64_t)));
    memcpy((dut + dut_pc_off)/*dut.pc*/, &(qemu_r.pc), sizeof(uint64_t));
  }
}

注意:

在difftest_regcpy函数中,dut_pc_off表示pc变量在riscv64_CPU_state结构体中的偏移量,如果该结构体定义发生改变,则该偏移量可能发生变化,导致拷贝pc寄存器值出错。

3.7 让QEMU输出指令日志

在NEMU/tools/qemu-socket-diff/src/diff-test.c文件,将difftest_init函数中的execlp函数调用,修改为如下内容:

void difftest_init(int port) {
...
// qemu-system-riscv64 -S -gdb tcp::1234 -nographic -d in_asm -D ./qemu.log 
execlp(ISA_QEMU_BIN, ISA_QEMU_BIN, ISA_QEMU_ARGS "-S", "-gdb", buf, "-nographic", "-d", "in_asm", "-D", "./qemu.log", NULL);
...
}

备注:

只修改execlp所在那一行,其他不变。
主要是为了,在做DiffTest时,QEMU可以将运行过的指令,打印到日志中,以便QEMU与NEMU不同步时,进行对比分析。

3.8 编译生成riscv64-qemu-so

进入NEMU/tools/qemu-socket-difftest目录

cd tools/qemu-socket-difftest/

编译qemu-socket-difftest

make -j`nproc`

在NEMU/tools/qemu-socket-diff/build目录下,生成riscv64-qemu-so动态库。

4 以DiffTest方式,运行NEMU

我们还需要,将以下文件,拷贝到NEMU/build目录下。

  • riscv64-qemu-so:上面生成DiffTest的socket动态库。
  • benos_payload.bin:MySBI+BenOS二进制文件,生成方法可参考《NEMU模拟器源码编译与使用》第2节内容。
  • qemu-system-riscv64:QEMU可执行程序,生成方法可参考《在QEMU上运行OpenSBI+Linux+Rootfs》第1节内容。
  • opensbi-riscv64-generic-fw_dynamic.bin:QEMU RISCV64默认的OpenSBI二进制文件,可从QEMU源码路径:qemu-7.1.0/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin获得;QEMU启动时,默认会加载该文件,没有该文件的话,启动时会报错。

以DiffTest方式,运行NEMU:

./riscv64-nemu-interpreter -b benos_payload.bin -d ./riscv64-qemu-so -l nemu.log

NEMU会fork一个子进程,去启动QEMU。
QEMU启动成功后,NEMU会将benos_payload.bin二进制代码,发送到QEMU中,随后两边就可以一起运行,并逐条指令进行结果对比,一旦两边结果不一致,就会触发异常停下来,期间执行过程,会写入nemu.log文件。

4.1 DiffTest检测到结果相同

运行效果,如下:
在这里插入图片描述
我们的BenOS,通过串口打印出了信息:Welcome RISC-V!

我们再看nemu.log,如下:
在这里插入图片描述
证明NEMU与QEMU,运行MySBI+BenOS二进制文件,每一条指令的运行结果,都是一样的,因此DiffTest未报错。

4.2 DiffTest检测到结果不同

假设DiffTest检测到,运行结果不一致时,其运行效果,如下:
在这里插入图片描述
我们的BenOS,没有打印出Welcome RISC-V!,却打印出了CPU当前寄存器值。

我们再看nemu.log,如下:
在这里插入图片描述
在difftest.h文件第49行,difftest_check_reg函数报错:

pc is different after executing instruction at pc = 0x00000000800000a8, right = 0x0000000000000000, wrong = 0x0000000080200000

也就是说,在执行mret指令后,QEMU与NEMU中两边寄存器值不相等:

  • pc is different after…:表示是PC寄存器的值不一样,如果是ra寄存器,这里就会显示为ra。
  • right:表示QEMU中值,为0。
  • wrong:表示NEMU中值,为0x80200000。

两边不一致,因此报错,我们就需要去分析,看看是哪里有问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1922138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构之单链表在不带标准头的情况下C,C#,C++分别怎么实现?

文章目录 单链表的概念单链表的操作单链表不带标准头结点示例C语言实现C#实现C实现 数据结构是计算机科学中非常重要的一部分&#xff0c;它帮助我们理解和操作数据。单链表是数据结构中的一种基本类型&#xff0c;它由一系列节点组成&#xff0c;每个节点包含数据域和指向列表…

强化学习编程实践-4-基于蒙特卡洛的方法

第3章给出了学习算法的基本思路&#xff1a;策略评估和策略改善。其中策略评估用到了以下的公式&#xff08;4.1&#xff09;&#xff1a; 策略改善则用了最简单的贪婪策略&#xff08;4.2&#xff09;&#xff1a; 为什么要用蒙特卡洛算法&#xff1f;先看公式4.1和4.2&#x…

图鸟UI框架在uni-app多端应用开发中的实践与应用

摘要&#xff1a; 随着移动互联网的蓬勃发展&#xff0c;跨平台应用开发已成为行业趋势。本文将探讨图鸟UI框架如何在uni-app开发环境下助力开发者高效构建多端应用&#xff0c;并通过具体案例展示其在实际项目中的应用效果。 一、引言 在移动应用开发领域&#xff0c;跨平台…

【错题集-编程题】栈和排序(栈 + 贪心)

牛客对于题目连接&#xff1a;栈和排序_牛客题霸_牛客网 (nowcoder.com) 一、分析题目 每次尽可能的先让当前需要的最大值弹出去。 二、代码 // 修改后的代码 class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方…

百度文心4.0 Turbo开放,领跑国内AI大模型赛道!

百度文心4.0 Turbo开放&#xff0c;领跑国内AI大模型赛道&#xff01; 前言 文心一言大模型 就在7月5日&#xff0c;在2024世界人工智能大会 (WAIC) 上&#xff0c;百度副总裁谢广军宣布文心大模型4.0 Turbo正式向企业客户全面开放&#xff01;这一举动直接引发了业界的关注。那…

uniapp微信小程序 TypeError: $refs[ref].push is not a function

我的写法 this.$refs.addPopup.open();报错 打印出来是这样的 解决 参考未整理 原因 在当前页面使用的v-for循环 并且循环体内也有组件使用了ref&#xff08;而我没有把每个ref做区别命名&#xff09; 这样就导致了我有很多同名的ref&#xff0c;然后就报错了 解决办法&a…

上海-三轮摩托车—D证科目四 错题记录

目录 1.安全驾驶题 2.道路图标题 3.天气类情景题 4.事故处理类题 上海-三轮摩托车—D证科目一 错题记录: 1.安全驾驶题

阐述 C 语言中的浮点数精度问题?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&…

tableau回归分析与时间序列分析 - 17

回归分析与时间序列分析 1. 回归分析1.1 线性回归绘制1.1.1 导入数据1.1.2 基本绘制1.1.3 趋势线显示-方法一1.1.4 趋势线显示-方法二1.1.5 趋势线显示-方法三1.1.6 添加区域注释 1.2 指数回归绘制1.2.1 创建字段1.2.2 异常区分1.2.3 创建参数1.2.4 异常区分字段修改1.2.5 显示…

MySQL学习笔记 下

MySQL学习笔记&#xff08;需接上篇&#xff09; 1. 通配符 用于替换字符串中的一个或多个字符&#xff1b;通配符常与LIKE关键字一起使用&#xff0c;用于搜索字段中的指定模式&#xff1b; 2. 别名 用于为表或表中的列提供临时名称&#xff1b; 别名用于为表或表中的字段…

【安卓学习】状态开关按钮

Switch 状态开关按钮&#xff08;Switch&#xff09;也是由 Button 派生出来的&#xff0c;所以在本质上它也算是一个比较高级的按钮&#xff0c;用户可以来回拖动“拇指”控制该按钮的开启与关闭或者只需轻按&#xff0c;就像选择复选框一样来开启该组件。 基本语法 <Sw…

外贸淡季如何应对?九大技巧告诉你!

许多外贸人都知道&#xff0c;7-9月是外贸的淡季&#xff0c;主要是因为西方国家很多公司会放假&#xff0c;或者轮流放。欧美人扎堆度假去了&#xff0c;欧洲城市街头的人数会减少很多。导致很多订单暂缓。所以这三个月经常被称为是外贸“淡季”。 大体上讲&#xff0c;外贸的…

警钟!电池储能安全事故频发!物联网技术如何加强储能安全排查?

在新能源时代背景下&#xff0c;储能系统作为能源转型的关键支撑技术&#xff0c;其安全问题日益凸显&#xff0c;尤其是近期海外电池项目连续发生的事故&#xff0c;为全球储能行业敲响了警钟。面对这一挑战&#xff0c;物联网技术以其强大的数据采集、智能分析与远程监控能力…

聊聊mysql

记录那些坑 本文会持续更新&#xff0c;陆续更新有关mysql技术内幕、实战优化、面试技巧。 文章目录 前言索引BTree之聚集索引BTree之辅助索引BTree之联合索引BTree之覆盖索引 使用到的工具1、py_innodb_page_info工具2、hexdump工具 总结 前言 重中之重的MySql数据库 mysql…

ARM功耗管理之睡眠锁

安全之安全(security)博客目录导读 思考&#xff1a;什么是睡眠锁&#xff1f;什么是唤醒源&#xff1f;什么是组合唤醒源&#xff1f; Kernel wakelocks的功能&#xff1a; 1&#xff09;允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠。 2&#xff09;wake_lo…

SpringBoot新手快速入门系列教程7:基于Redis的一个简单存取数据的例子

我的教程都是亲自测试可行才发布的&#xff0c;如果有任何问题欢迎留言或者来群里我每天都会解答。 新手可能有这样的疑问&#xff0c;有了数据库的存取方式&#xff0c;我们为什么还要使用Redis这种缓存数据库读取方式呢&#xff1f; 原因主要有以下几点&#xff1a; 1. 性能…

本地部署,MODNet 背景去除大模型

目录 摘要 引言 MODNet 架构 关键组件 技术原理 本地部署 运行结果 结论 GitHub - ZHKKKe/MODNet: A Trimap-Free Portrait Matting Solution in Real Time [AAAI 2022]A Trimap-Free Portrait Matting Solution in Real Time [AAAI 2022] - ZHKKKe/MODNethttps://gith…

在Linux上导出NFS共享---网络文件系统

目录 一、NFS介绍 二、NFS 所需要的服务 三、搭建NFS服务器共享文件到客户端 在虚拟机129上配置&#xff1a; 在虚拟机135上配置 测试 四、autofs自动挂载 1、安装软件启动 2、编写 /etc/auto.master,在里面添加内容如下 3、编写 /etc/auto.nfs&#xff0c;其内容如下 …

怎么提高音频的播放速度?可以提高音频播放速度的四种方法推荐

怎么提高音频的播放速度&#xff1f;提高音频的播放速度是一种有效的策略&#xff0c;可以显著节省时间和提升信息获取的效率。随着信息量不断增加和学习需求的多样化&#xff0c;快速播放音频已成为许多人在日常生活和工作中的常见做法。这种方法不仅可以用于提高学习效率&…

基于 JSP 的网络招标系统设计与实现

点击下载源码 基于jsp的网上招标系统设计与实现 摘要 从本世纪初&#xff0c;互联网开始加速发展&#xff0c;各种创新型应用和互联网新概念不断出现&#xff0c;例如搜索引擎、电子商务、博客、维基百科、RSS、3G、web2.0、长尾理论等。这些应用和概念与知识管理都有着或多或少…