refactor: drop internal difftest support

This commit is contained in:
xinyangli 2024-07-23 17:15:35 +08:00
parent f852ee8689
commit 422ff9e006
Signed by: xin
SSH key fingerprint: SHA256:qZ/tzd8lYRtUFSrfBDBMcUqV4GHKxqeqRA3huItgvbk
21 changed files with 192 additions and 433 deletions

View file

@ -16,7 +16,6 @@
#include "gdbstub.h"
#include <cpu/cpu.h>
#include <cpu/decode.h>
#include <cpu/difftest.h>
#include <locale.h>
#include <utils.h>

View file

@ -1,132 +0,0 @@
/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <dlfcn.h>
#include <isa.h>
#include <cpu/cpu.h>
#include <memory/paddr.h>
#include <utils.h>
#include <difftest-def.h>
void (*ref_difftest_memcpy)(paddr_t addr, void *buf, size_t n, bool direction) = NULL;
void (*ref_difftest_regcpy)(void *dut, bool direction) = NULL;
void (*ref_difftest_exec)(uint64_t n) = NULL;
void (*ref_difftest_raise_intr)(uint64_t NO) = NULL;
#ifdef CONFIG_DIFFTEST
static bool is_skip_ref = false;
static int skip_dut_nr_inst = 0;
// this is used to let ref skip instructions which
// can not produce consistent behavior with NEMU
void difftest_skip_ref() {
is_skip_ref = true;
// If such an instruction is one of the instruction packing in QEMU
// (see below), we end the process of catching up with QEMU's pc to
// keep the consistent behavior in our best.
// Note that this is still not perfect: if the packed instructions
// already write some memory, and the incoming instruction in NEMU
// will load that memory, we will encounter false negative. But such
// situation is infrequent.
skip_dut_nr_inst = 0;
}
// this is used to deal with instruction packing in QEMU.
// Sometimes letting QEMU step once will execute multiple instructions.
// We should skip checking until NEMU's pc catches up with QEMU's pc.
// The semantic is
// Let REF run `nr_ref` instructions first.
// We expect that DUT will catch up with REF within `nr_dut` instructions.
void difftest_skip_dut(int nr_ref, int nr_dut) {
skip_dut_nr_inst += nr_dut;
while (nr_ref -- > 0) {
ref_difftest_exec(1);
}
}
void init_difftest(char *ref_so_file, long img_size, int port) {
assert(ref_so_file != NULL);
void *handle;
handle = dlopen(ref_so_file, RTLD_LAZY);
assert(handle);
ref_difftest_memcpy = dlsym(handle, "difftest_memcpy");
assert(ref_difftest_memcpy);
ref_difftest_regcpy = dlsym(handle, "difftest_regcpy");
assert(ref_difftest_regcpy);
ref_difftest_exec = dlsym(handle, "difftest_exec");
assert(ref_difftest_exec);
ref_difftest_raise_intr = dlsym(handle, "difftest_raise_intr");
assert(ref_difftest_raise_intr);
void (*ref_difftest_init)(int) = dlsym(handle, "difftest_init");
assert(ref_difftest_init);
Log("Differential testing: %s", ANSI_FMT("ON", ANSI_FG_GREEN));
Log("The result of every instruction will be compared with %s. "
"This will help you a lot for debugging, but also significantly reduce the performance. "
"If it is not necessary, you can turn it off in menuconfig.", ref_so_file);
ref_difftest_init(port);
ref_difftest_memcpy(RESET_VECTOR, guest_to_host(RESET_VECTOR), img_size, DIFFTEST_TO_REF);
ref_difftest_regcpy(&cpu, DIFFTEST_TO_REF);
}
static void checkregs(CPU_state *ref, vaddr_t pc) {
if (!isa_difftest_checkregs(ref, pc)) {
nemu_state.state = NEMU_ABORT;
nemu_state.halt_pc = pc;
isa_reg_display();
}
}
void difftest_step(vaddr_t pc, vaddr_t npc) {
CPU_state ref_r;
if (skip_dut_nr_inst > 0) {
ref_difftest_regcpy(&ref_r, DIFFTEST_TO_DUT);
if (ref_r.pc == npc) {
skip_dut_nr_inst = 0;
checkregs(&ref_r, npc);
return;
}
skip_dut_nr_inst --;
if (skip_dut_nr_inst == 0)
panic("can not catch up with ref.pc = " FMT_WORD " at pc = " FMT_WORD, ref_r.pc, pc);
return;
}
if (is_skip_ref) {
// to skip the checking of an instruction, just copy the reg state to reference design
ref_difftest_regcpy(&cpu, DIFFTEST_TO_REF);
is_skip_ref = false;
return;
}
ref_difftest_exec(1);
ref_difftest_regcpy(&ref_r, DIFFTEST_TO_DUT);
checkregs(&ref_r, pc);
}
#else
void init_difftest(char *ref_so_file, long img_size, int port) { }
#endif

View file

@ -1,51 +0,0 @@
/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include "types.h"
#include <cpu/cpu.h>
#include <cpu/decode.h>
#include <difftest-def.h>
#include <isa.h>
#include <memory/paddr.h>
__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n,
bool direction) {
if (direction == DIFFTEST_TO_REF) {
memcpy(guest_to_host(addr), buf, n);
} else {
memcpy(buf, guest_to_host(addr), n);
}
}
__EXPORT void difftest_regcpy(void *dut, bool direction) {
// assert(0);
if (direction == DIFFTEST_TO_DUT)
memcpy(dut, &cpu, sizeof(CPU_state));
else
memcpy(&cpu, dut, sizeof(CPU_state));
}
__EXPORT void difftest_exec(uint64_t n) { cpu_exec(n); }
__EXPORT void difftest_raise_intr(word_t NO) {
// assert(0);
}
__EXPORT void difftest_init(int port) {
void init_mem();
init_mem();
/* Perform ISA dependent initialization. */
init_isa();
}

View file

@ -13,6 +13,8 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <assert.h>
#include <debug.h>
#include <device/map.h>
#define PORT_IO_SPACE_MAX 65535
@ -22,15 +24,19 @@ static IOMap maps[NR_MAP] = {};
static int nr_map = 0;
/* device interface */
void add_pio_map(const char *name, ioaddr_t addr, void *space, uint32_t len, io_callback_t callback) {
void add_pio_map(const char *name, ioaddr_t addr, void *space, uint32_t len,
io_callback_t callback) {
assert(nr_map < NR_MAP);
assert(addr + len <= PORT_IO_SPACE_MAX);
maps[nr_map] = (IOMap){ .name = name, .low = addr, .high = addr + len - 1,
.space = space, .callback = callback };
maps[nr_map] = (IOMap){.name = name,
.low = addr,
.high = addr + len - 1,
.space = space,
.callback = callback};
Log("Add port-io map '%s' at [" FMT_PADDR ", " FMT_PADDR "]",
maps[nr_map].name, maps[nr_map].low, maps[nr_map].high);
nr_map ++;
nr_map++;
}
/* CPU interface */

View file

@ -13,8 +13,10 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <utils.h>
#include <assert.h>
#include <debug.h>
#include <device/map.h>
#include <utils.h>
/* http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming */
// NOTE: this is compatible to 16550
@ -23,7 +25,6 @@
static uint8_t *serial_base = NULL;
static void serial_putc(char ch) {
MUXDEF(CONFIG_TARGET_AM, putch(ch), putc(ch, stderr));
}
@ -31,21 +32,23 @@ static void serial_putc(char ch) {
static void serial_io_handler(uint32_t offset, int len, bool is_write) {
assert(len == 1);
switch (offset) {
/* We bind the serial port with the host stderr in NEMU. */
case CH_OFFSET:
if (is_write) serial_putc(serial_base[0]);
else panic("do not support read");
break;
default: panic("do not support offset = %d", offset);
/* We bind the serial port with the host stderr in NEMU. */
case CH_OFFSET:
if (is_write)
serial_putc(serial_base[0]);
else
panic("do not support read");
break;
default:
panic("do not support offset = %d", offset);
}
}
void init_serial() {
serial_base = new_space(8);
#ifdef CONFIG_HAS_PORT_IO
add_pio_map ("serial", CONFIG_SERIAL_PORT, serial_base, 8, serial_io_handler);
add_pio_map("serial", CONFIG_SERIAL_PORT, serial_base, 8, serial_io_handler);
#else
add_mmio_map("serial", CONFIG_SERIAL_MMIO, serial_base, 8, serial_io_handler);
#endif
}

View file

@ -13,8 +13,9 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <device/map.h>
#include <assert.h>
#include <device/alarm.h>
#include <device/map.h>
#include <utils.h>
static uint32_t *rtc_port_base = NULL;
@ -40,7 +41,7 @@ static void timer_intr() {
void init_timer() {
rtc_port_base = (uint32_t *)new_space(8);
#ifdef CONFIG_HAS_PORT_IO
add_pio_map ("rtc", CONFIG_RTC_PORT, rtc_port_base, 8, rtc_io_handler);
add_pio_map("rtc", CONFIG_RTC_PORT, rtc_port_base, 8, rtc_io_handler);
#else
add_mmio_map("rtc", CONFIG_RTC_MMIO, rtc_port_base, 8, rtc_io_handler);
#endif

View file

@ -13,20 +13,19 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <utils.h>
#include <cpu/ifetch.h>
#include <difftest.h>
#include <isa.h>
#include <cpu/difftest.h>
#include <utils.h>
void set_nemu_state(int state, vaddr_t pc, int halt_ret) {
difftest_skip_ref();
nemu_do_difftest = false;
nemu_state.state = state;
nemu_state.halt_pc = pc;
nemu_state.halt_ret = halt_ret;
}
__attribute__((noinline))
void invalid_inst(vaddr_t thispc) {
__attribute__((noinline)) void invalid_inst(vaddr_t thispc) {
uint32_t temp[2];
vaddr_t pc = thispc;
temp[0] = inst_fetch(&pc, 4);
@ -34,18 +33,24 @@ void invalid_inst(vaddr_t thispc) {
uint8_t *p = (uint8_t *)temp;
printf("invalid opcode(PC = " FMT_WORD "):\n"
"\t%02x %02x %02x %02x %02x %02x %02x %02x ...\n"
"\t%08x %08x...\n",
thispc, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], temp[0], temp[1]);
"\t%02x %02x %02x %02x %02x %02x %02x %02x ...\n"
"\t%08x %08x...\n",
thispc, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], temp[0],
temp[1]);
printf("There are two cases which will trigger this unexpected exception:\n"
"1. The instruction at PC = " FMT_WORD " is not implemented.\n"
"2. Something is implemented incorrectly.\n", thispc);
printf("Find this PC(" FMT_WORD ") in the disassembling result to distinguish which case it is.\n\n", thispc);
"1. The instruction at PC = " FMT_WORD " is not implemented.\n"
"2. Something is implemented incorrectly.\n",
thispc);
printf("Find this PC(" FMT_WORD
") in the disassembling result to distinguish which case it is.\n\n",
thispc);
printf(ANSI_FMT("If it is the first case, see\n%s\nfor more details.\n\n"
"If it is the second case, remember:\n"
"* The machine is always right!\n"
"* Every line of untested code is always wrong!\n\n", ANSI_FG_RED), isa_logo);
"If it is the second case, remember:\n"
"* The machine is always right!\n"
"* Every line of untested code is always wrong!\n\n",
ANSI_FG_RED),
isa_logo);
set_nemu_state(NEMU_ABORT, thispc, -1);
}

View file

@ -17,17 +17,22 @@
#include <cpu/cpu.h>
#include <gdbstub.h>
void sdb_mainloop();
int gdbstub_loop();
extern bool enable_gdbstub;
void engine_start() {
#ifdef CONFIG_TARGET_AM
cpu_exec(-1);
#else
/* Receive commands from user. */
int nemu_gdbstub_run();
if (nemu_gdbstub_run()) {
Error("gdbstub exited abnormally");
exit(1);
int ret = 0;
if (enable_gdbstub) {
if ((ret = gdbstub_loop())) {
Error("gdbstub exited abnormally");
exit(ret);
}
} else {
cpu_exec(-1);
}
#endif
}

View file

@ -1,28 +0,0 @@
/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <isa.h>
#include <cpu/difftest.h>
#include "../local-include/reg.h"
bool isa_difftest_checkregs(CPU_state *ref_r, vaddr_t pc) {
for(int i = 0; i < MUXDEF(CONFIG_RVE, 16, 32); i++) {
if(!difftest_check_reg(reg_name(i), pc, ref_r->gpr[i], gpr(i))) return false;
}
return true;
}
void isa_difftest_attach() {
}

View file

@ -15,7 +15,6 @@
#include "local-include/reg.h"
#include "macro.h"
#include <difftest-def.h>
#include <errno.h>
#include <gdbstub.h>
#include <isa.h>

View file

@ -4,7 +4,6 @@
extern "C" {
#include <cpu/cpu.h>
#include <debug.h>
#include <difftest-def.h>
#include <errno.h>
#include <gdbstub.h>
#include <isa.h>
@ -17,6 +16,10 @@ typedef struct {
bool halt;
} DbgState;
__EXPORT size_t nemu_dbg_state_size = sizeof(DbgState);
__EXPORT bool nemu_do_difftest = true;
__EXPORT arch_info_t nemu_isa_arch_info;
__EXPORT int nemu_read_mem(void *args, size_t addr, size_t len, void *val) {
if (!in_pmem(addr))
return EINVAL;
@ -109,7 +112,6 @@ __EXPORT int nemu_read_reg(void *args, int regno, size_t *data) {
__EXPORT int nemu_write_reg(void *args, int regno, size_t data) {
return isa_write_reg(args, regno, data);
}
__EXPORT size_t argsize = sizeof(DbgState);
static struct target_ops nemu_gdbstub_ops = {.cont = nemu_cont,
.stepi = nemu_stepi,
@ -120,35 +122,41 @@ static struct target_ops nemu_gdbstub_ops = {.cont = nemu_cont,
.set_bp = nemu_set_bp,
.del_bp = nemu_del_bp,
.on_interrupt = NULL};
static DbgState dbg;
static DbgState *pdbg;
static gdbstub_t gdbstub_priv;
const char SOCKET_ADDR[] = "/tmp/gdbstub-nemu.sock";
__EXPORT void nemu_init(void *args) {
static void init_remote_gdbstub(void *args) {
DbgState *dbg_state = (DbgState *)args;
pdbg = (DbgState *)args;
dbg_state->bp = new std::vector<breakpoint_t>();
dbg_state->halt = 0;
Assert(dbg_state->bp != NULL, "Failed to allocate breakpoint");
}
__EXPORT void nemu_init(void *args) {
init_remote_gdbstub(args);
void init_rand();
void init_mem();
IFDEF(CONFIG_DEVICE, void init_device());
init_rand();
init_mem();
IFDEF(CONFIG_DEVICE, init_device());
/* Perform ISA dependent initialization. */
init_isa();
}
__EXPORT int nemu_gdbstub_init() {
dbg.bp = new std::vector<breakpoint_t>();
assert(dbg.bp);
int gdbstub_loop() {
if (!gdbstub_init(&gdbstub_priv, &nemu_gdbstub_ops,
(arch_info_t)isa_arch_info, strdup(SOCKET_ADDR))) {
(arch_info_t)isa_arch_info, SOCKET_ADDR)) {
return EINVAL;
}
return 0;
}
__EXPORT int nemu_gdbstub_run() {
printf("Waiting for gdb connection at %s", SOCKET_ADDR);
bool success = gdbstub_run(&gdbstub_priv, &dbg);
bool success = gdbstub_run(&gdbstub_priv, pdbg);
// gdbstub_close(&gdbstub_priv);
return !success;
}

View file

@ -13,9 +13,11 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <cpu/cpu.h>
#include <errno.h>
#include <isa.h>
#include <memory/paddr.h>
#include <nemu.h>
#include <strings.h>
#include <utils.h>
@ -25,7 +27,11 @@ void init_mem();
void init_difftest(char *ref_so_file, long img_size, int port);
void init_device();
void init_disasm(const char *triple);
void nemu_init();
char *log_file = NULL;
char *elf_file = NULL;
char *img_file = NULL;
bool enable_gdbstub = false;
static void welcome() {
Log("Trace: %s", MUXDEF(CONFIG_TRACE, ANSI_FMT("ON", ANSI_FG_GREEN),
@ -43,12 +49,6 @@ static void welcome() {
#ifndef CONFIG_TARGET_AM
#include <getopt.h>
static char *log_file = NULL;
static char *elf_file = NULL;
static char *diff_so_file = NULL;
static char *img_file = NULL;
static int difftest_port = 1234;
static long load_img() {
FILE *fp = NULL;
size_t img_filename_len = strlen(img_file);
@ -113,38 +113,30 @@ static long load_img() {
static int parse_args(int argc, char *argv[]) {
const struct option table[] = {
{"batch", no_argument, NULL, 'b'},
{"log", required_argument, NULL, 'l'},
{"diff", required_argument, NULL, 'd'},
{"port", required_argument, NULL, 'p'},
{"elf", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{0, 0, NULL, 0},
{"batch", no_argument, NULL, 'b'}, {"log", required_argument, NULL, 'l'},
{"debug", no_argument, NULL, 'g'}, {"elf", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'}, {0, 0, NULL, 0},
};
int o;
while ((o = getopt_long(argc, argv, "-bhl:d:p:", table, NULL)) != -1) {
switch (o) {
case 'p':
sscanf(optarg, "%d", &difftest_port);
break;
case 'l':
log_file = optarg;
break;
case 'd':
diff_so_file = optarg;
break;
case 'f':
elf_file = optarg;
break;
case 'g':
enable_gdbstub = true;
break;
case 1:
img_file = optarg;
return 0;
default:
printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]);
printf("\t-b,--batch run with batch mode\n");
printf("\t-g,--debug enable gdb remote server\n");
printf("\t-l,--log=FILE output log to FILE\n");
printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n");
printf("\t-p,--port=PORT run DiffTest with port PORT\n");
printf("\t-f,--elf=FILE elf file with debug info\n");
printf("\n");
exit(0);
@ -154,38 +146,18 @@ static int parse_args(int argc, char *argv[]) {
}
void init_monitor(int argc, char *argv[]) {
/* Perform some global initialization. */
/* Parse arguments. */
parse_args(argc, argv);
/* Set random seed. */
init_rand();
void init_log(const char *log_file);
/* Open the log file. */
init_log(log_file);
/* Initialize memory. */
init_mem();
/* Initialize devices. */
IFDEF(CONFIG_DEVICE, init_device());
/* Perform ISA dependent initialization. */
init_isa();
/* Perform some global initialization. */
nemu_init(malloc(nemu_dbg_state_size));
/* Load the image to memory. This will overwrite the built-in image. */
long img_size = load_img();
/* Initialize differential testing. */
init_difftest(diff_so_file, img_size, difftest_port);
/* Initialize debugger */
// if (nemu_init()) {
// Error("Failed to init");
// exit(1);
// }
nemu_init();
load_img();
// printf("elf_file: %s\n", elf_file);
if (elf_file != NULL) {