From 45e280469d51c4ddc259eee669fc9a2688766c20 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Thu, 15 Aug 2024 09:49:59 +0800 Subject: [PATCH] feat: add mini-gdbstub api support --- CMakeLists.txt | 1 + flake.nix | 1 + lib/CMakeLists.txt | 7 +- lib/spike-diff.cpp | 302 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 lib/spike-diff.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ca09789..036f787 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,5 +7,6 @@ set(CMAKE_C_STANDARD 17) include(FindPkgConfig) pkg_check_modules(riscv-riscv REQUIRED IMPORTED_TARGET riscv-riscv) pkg_check_modules(riscv-fesvr REQUIRED IMPORTED_TARGET riscv-fesvr) +find_package(spdlog REQUIRED) add_subdirectory(lib) diff --git a/flake.nix b/flake.nix index 6d3961f..6632a41 100644 --- a/flake.nix +++ b/flake.nix @@ -53,6 +53,7 @@ doInstallCheck = false; })) mini-gdbstub + spdlog cmake pkg-config ninja diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6922ca0..23d6f98 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -2,5 +2,8 @@ add_library(spike-diff SHARED spike-diff.cpp) target_link_libraries(spike-diff PRIVATE - PkgConfig::riscv-riscv - PkgConfig::riscv-fesvr) + PkgConfig::riscv-riscv PkgConfig::riscv-fesvr + spdlog::spdlog) + +set_property(TARGET PROPERTY POSITION_INDEPENDENT_CODE ON) +target_link_options(spike-diff PRIVATE -Wl,-E) diff --git a/lib/spike-diff.cpp b/lib/spike-diff.cpp new file mode 100644 index 0000000..86edf27 --- /dev/null +++ b/lib/spike-diff.cpp @@ -0,0 +1,302 @@ +// Mostly copied from https://github.com/rivosinc/hammer + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Difftest { +public: + std::shared_ptr logger = + spdlog::stdout_color_mt("spike-diff"); + + Difftest(); + + template + std::optional> + get_mem(addr_t virtual_address, size_t bytes_to_read, uint8_t hart_id = 0); + + template + int set_mem(addr_t virtual_address, const std::vector &data, + uint8_t hart_id = 0); + + uint32_t set_breakpoint(addr_t addr, uint8_t hart_id = 0); + + reg_t get_gpr(uint8_t gpr_id, uint8_t hart_id = 0); + void set_gpr(uint8_t gpr_id, reg_t new_gpr_value, uint8_t hart_id = 0); + + reg_t get_pc(uint8_t hart_id = 0); + void set_pc(reg_t new_pc_value, uint8_t hart_id = 0); + + void single_step(uint8_t hart_id = 0); + std::optional cont(void); + void get_state(uint8_t hart_id = 0); + +private: + sim_t *simulator; +}; + +Difftest::Difftest() { + debug_module_config_t dmconfig = {.progbufsize = 2, + .max_sba_data_width = 0, + .require_authentication = false, + .abstract_rti = 0, + .support_hasel = true, + .support_abstract_csr_access = true, + .support_abstract_fpr_access = true, + .support_haltgroups = true, + .support_impebreak = true}; + std::vector> mem( + 1, std::make_pair(reg_t(DRAM_BASE), new mem_t(0x8000000))); + + std::vector plugin_devices_factories; + + cfg_t cfg; + cfg.initrd_bounds = std::make_pair((reg_t)0, (reg_t)0), + cfg.bootargs = nullptr; + cfg.isa = "RV32I"; + cfg.priv = "M"; + cfg.misaligned = false; + cfg.endianness = endianness_little; + cfg.pmpregions = 0; + cfg.pmpgranularity = 4; + cfg.mem_layout = std::vector(); + cfg.hartids = std::vector(1, 0); + cfg.explicit_hartids = false; + cfg.real_time_clint = false; + cfg.trigger_count = 4; + + simulator = new sim_t{&cfg, + false, + mem, + plugin_devices_factories, + std::vector(1, ""), + dmconfig, + nullptr, + false, + nullptr, + false, + nullptr}; + simulator->set_debug(false); + processor_t *hart = simulator->get_core(0); + hart->reset(); + // spdlog::trace("Sim: {} {}", simulator->get_core(0))) +} + +template +std::optional> Difftest::get_mem(addr_t virtual_address, + size_t bytes_to_read, + uint8_t hart_id) { + mmu_t *mmu = simulator->get_core(hart_id)->get_mmu(); + std::vector data; + try { + for (size_t i = 0; i < bytes_to_read; i += sizeof(T)) + data.push_back(mmu->load(virtual_address + i)); + } catch (trap_t &t) { + return std::nullopt; + } + return data; +} + +template +int Difftest::set_mem(addr_t virtual_address, const std::vector &data, + uint8_t hart_id) { + mmu_t *mmu = simulator->get_core(hart_id)->get_mmu(); + + logger->trace("(W) {:x} data size: {:x}", virtual_address, data.size()); + try { + for (auto &i : data) { + mmu->store(virtual_address, i); + virtual_address += sizeof(T); + } + } catch (trap_t &t) { + return EACCES; + } + return 0; +} + +uint32_t Difftest::set_breakpoint(addr_t addr, uint8_t hart_id) { + uint32_t original_inst = get_mem(addr, 4, hart_id).value().at(0); + const std::vector EBREAK{0x100073U}; + // assert(set_mem(addr, EBREAK, hart_id) == 0); + return original_inst; +} + +reg_t Difftest::get_gpr(uint8_t gpr_id, uint8_t hart_id) { + assert(gpr_id < NXPR); + + processor_t *hart = simulator->get_core(hart_id); + state_t *hart_state = hart->get_state(); + + return hart_state->XPR[gpr_id]; +} + +void Difftest::set_gpr(uint8_t gpr_id, reg_t new_gpr_value, uint8_t hart_id) { + assert(gpr_id < NXPR); + + processor_t *hart = simulator->get_core(hart_id); + state_t *hart_state = hart->get_state(); + + hart_state->XPR.write(gpr_id, new_gpr_value); +} + +reg_t Difftest::get_pc(uint8_t hart_id) { + processor_t *hart = simulator->get_core(hart_id); + state_t *hart_state = hart->get_state(); + + return hart_state->pc; +} + +void Difftest::set_pc(reg_t new_pc_value, uint8_t hart_id) { + processor_t *hart = simulator->get_core(hart_id); + state_t *hart_state = hart->get_state(); + + hart_state->pc = new_pc_value; +} + +void Difftest::single_step(uint8_t hart_id) { + processor_t *hart = simulator->get_core(hart_id); + hart->step(1); +} + +std::optional Difftest::cont(void) { exit(1); } + +void Difftest::get_state(uint8_t hart_id) { + processor_t *hart = simulator->get_core(hart_id); + hart->halted(); +} + +extern "C" { +typedef uint32_t word_t; + +struct DbgState { + Difftest *diff; + struct Breakpoint { + addr_t addr; + uint32_t original_instruction; + + bool operator==(const Breakpoint &b) { return this->addr == b.addr; } + }; + std::vector breakpoints; +}; + +arch_info_t spike_isa_arch_info = { + .target_desc = TARGET_RV32, + .reg_num = 32, + .reg_byte = 4, +}; +bool spike_do_difftest = true; +bool spike_dbg_state_size = sizeof(DbgState); + +int spike_init(void *args) { + DbgState *dbg = (DbgState *)args; + dbg->diff = new Difftest{}; + return dbg->diff != nullptr; +} + +int spike_read_mem(void *args, size_t addr, size_t len, void *val) { + DbgState *dbg = (DbgState *)args; + + try { + std::vector mem{dbg->diff->get_mem(addr, len).value()}; + memcpy(val, mem.data(), len); + } catch (std::bad_optional_access) { + return EACCES; + } + + return 0; +} + +int spike_write_mem(void *args, size_t addr, size_t len, void *val) { + DbgState *dbg = (DbgState *)args; + size_t dlen = len / sizeof(word_t); + word_t *dataptr = (word_t *)val; + std::vector data{dataptr, dataptr + dlen}; + + word_t remains = 0; + memcpy(&remains, dataptr + dlen, len % sizeof(word_t)); + if (len % sizeof(word_t)) + data.push_back(remains); + + return dbg->diff->set_mem(addr, data); +} + +int spike_read_reg(void *args, int regno, size_t *value) { + DbgState *dbg = (DbgState *)args; + if (regno == 32) { + *value = (word_t)dbg->diff->get_pc(); + } else { + *value = (word_t)dbg->diff->get_gpr(regno); + } + return 0; +} + +int spike_write_reg(void *args, int regno, size_t value) { + DbgState *dbg = (DbgState *)args; + if (regno == 32) { + dbg->diff->set_pc(value); + } else { + dbg->diff->set_gpr(regno, value); + } + return 0; +} + +void spike_stepi(void *args, gdb_action_t *res) { + DbgState *dbg = (DbgState *)args; + static bool on_breakpoint = false; + if (!on_breakpoint) { + for (const auto &bp : dbg->breakpoints) { + if ((word_t)dbg->diff->get_pc() == bp.addr) { + res->reason = gdb_action_t::ACT_BREAKPOINT; + on_breakpoint = true; + return; + } else if ((word_t)dbg->diff->get_pc() == 0) { + res->reason = gdb_action_t::ACT_SHUTDOWN; + return; + } + } + } + dbg->diff->single_step(); + res->reason = gdb_action_t::ACT_NONE; + on_breakpoint = false; +} + +__attribute__((visibility("default"))) void spike_cont(void *args, + gdb_action_t *res) { + ; +} + +bool spike_set_bp(void *args, size_t addr, bp_type_t bp) { + DbgState *dbg = (DbgState *)args; + dbg->breakpoints.emplace_back( + DbgState::Breakpoint{addr, dbg->diff->set_breakpoint(addr)}); + return true; +} +__attribute__((visibility("default"))) void +spike_del_bp(void *args, size_t addr, bp_type_t type) { + DbgState *dbg = (DbgState *)args; + auto &bps = dbg->breakpoints; + auto it = std::find(bps.begin(), bps.end(), DbgState::Breakpoint{addr, type}); + std::swap(*it, *bps.rbegin()); + bps.pop_back(); +} + +__attribute__((visibility("default"))) void spike_on_interrupt() { ; } + +} // extern "C"