Files
openocd/src/target/riscv/riscv.c
T
Tim Newsome db754536e8 Support 64-bit FPRs on RV32.
Because there is no instruction that moves just half of a 64-bit FPR
to/from a GPR, we need to use scratch memory for this operation. This
code can theoretically use:
1. DMI_DATA, if it is memory mapped in the target.
2. DMI_PROGBUF, if it is writable in the target.
3. A user-configured address.

I have only tested this code very lightly. One reason is that gdb thinks
that on RV32 harts every register is 32 bits wide. Another is that this
is mostly proof-of-concept to satisfy the small program buffer code
review, which I don't want to drag out forever.

Existing tests don't realize that floating support was broken with
RV32D, and don't realize that it still doesn't work because of the gdb
problem mentioned above.

This change improves Issue #110 but there's more work to be done.

Change-Id: I99b8a36e5fea26f1d9e16e36cf99adc7be26b944
2017-10-27 13:15:22 -07:00

1690 lines
44 KiB
C

#include <assert.h>
#include <stdlib.h>
#include <time.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "target/target.h"
#include "target/algorithm.h"
#include "target/target_type.h"
#include "log.h"
#include "jtag/jtag.h"
#include "target/register.h"
#include "target/breakpoints.h"
#include "helper/time_support.h"
#include "riscv.h"
#include "gdb_regs.h"
#include "rtos/rtos.h"
/**
* Since almost everything can be accomplish by scanning the dbus register, all
* functions here assume dbus is already selected. The exception are functions
* called directly by OpenOCD, which can't assume anything about what's
* currently in IR. They should set IR to dbus explicitly.
*/
/**
* Code structure
*
* At the bottom of the stack are the OpenOCD JTAG functions:
* jtag_add_[id]r_scan
* jtag_execute_query
* jtag_add_runtest
*
* There are a few functions to just instantly shift a register and get its
* value:
* dtmcontrol_scan
* idcode_scan
* dbus_scan
*
* Because doing one scan and waiting for the result is slow, most functions
* batch up a bunch of dbus writes and then execute them all at once. They use
* the scans "class" for this:
* scans_new
* scans_delete
* scans_execute
* scans_add_...
* Usually you new(), call a bunch of add functions, then execute() and look
* at the results by calling scans_get...()
*
* Optimized functions will directly use the scans class above, but slightly
* lazier code will use the cache functions that in turn use the scans
* functions:
* cache_get...
* cache_set...
* cache_write
* cache_set... update a local structure, which is then synced to the target
* with cache_write(). Only Debug RAM words that are actually changed are sent
* to the target. Afterwards use cache_get... to read results.
*/
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
#define DIM(x) (sizeof(x)/sizeof(*x))
// Constants for legacy SiFive hardware breakpoints.
#define CSR_BPCONTROL_X (1<<0)
#define CSR_BPCONTROL_W (1<<1)
#define CSR_BPCONTROL_R (1<<2)
#define CSR_BPCONTROL_U (1<<3)
#define CSR_BPCONTROL_S (1<<4)
#define CSR_BPCONTROL_H (1<<5)
#define CSR_BPCONTROL_M (1<<6)
#define CSR_BPCONTROL_BPMATCH (0xf<<7)
#define CSR_BPCONTROL_BPACTION (0xff<<11)
#define DEBUG_ROM_START 0x800
#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4)
#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8)
#define DEBUG_RAM_START 0x400
#define SETHALTNOT 0x10c
/*** JTAG registers. ***/
#define DTMCONTROL 0x10
#define DTMCONTROL_DBUS_RESET (1<<16)
#define DTMCONTROL_IDLE (7<<10)
#define DTMCONTROL_ADDRBITS (0xf<<4)
#define DTMCONTROL_VERSION (0xf)
#define DBUS 0x11
#define DBUS_OP_START 0
#define DBUS_OP_SIZE 2
typedef enum {
DBUS_OP_NOP = 0,
DBUS_OP_READ = 1,
DBUS_OP_WRITE = 2
} dbus_op_t;
typedef enum {
DBUS_STATUS_SUCCESS = 0,
DBUS_STATUS_FAILED = 2,
DBUS_STATUS_BUSY = 3
} dbus_status_t;
#define DBUS_DATA_START 2
#define DBUS_DATA_SIZE 34
#define DBUS_ADDRESS_START 36
typedef enum {
RE_OK,
RE_FAIL,
RE_AGAIN
} riscv_error_t;
typedef enum slot {
SLOT0,
SLOT1,
SLOT_LAST,
} slot_t;
/*** Debug Bus registers. ***/
#define DMCONTROL 0x10
#define DMCONTROL_INTERRUPT (((uint64_t)1)<<33)
#define DMCONTROL_HALTNOT (((uint64_t)1)<<32)
#define DMCONTROL_BUSERROR (7<<19)
#define DMCONTROL_SERIAL (3<<16)
#define DMCONTROL_AUTOINCREMENT (1<<15)
#define DMCONTROL_ACCESS (7<<12)
#define DMCONTROL_HARTID (0x3ff<<2)
#define DMCONTROL_NDRESET (1<<1)
#define DMCONTROL_FULLRESET 1
#define DMINFO 0x11
#define DMINFO_ABUSSIZE (0x7fU<<25)
#define DMINFO_SERIALCOUNT (0xf<<21)
#define DMINFO_ACCESS128 (1<<20)
#define DMINFO_ACCESS64 (1<<19)
#define DMINFO_ACCESS32 (1<<18)
#define DMINFO_ACCESS16 (1<<17)
#define DMINFO_ACCESS8 (1<<16)
#define DMINFO_DRAMSIZE (0x3f<<10)
#define DMINFO_AUTHENTICATED (1<<5)
#define DMINFO_AUTHBUSY (1<<4)
#define DMINFO_AUTHTYPE (3<<2)
#define DMINFO_VERSION 3
/*** Info about the core being debugged. ***/
#define DBUS_ADDRESS_UNKNOWN 0xffff
// gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in
// its source tree. We must interpret the numbers the same here.
enum {
REG_XPR0 = 0,
REG_XPR31 = 31,
REG_PC = 32,
REG_FPR0 = 33,
REG_FPR31 = 64,
REG_CSR0 = 65,
REG_MSTATUS = CSR_MSTATUS + REG_CSR0,
REG_CSR4095 = 4160,
REG_PRIV = 4161,
REG_COUNT
};
#define MAX_HWBPS 16
#define DRAM_CACHE_SIZE 16
uint8_t ir_dtmcontrol[1] = {DTMCONTROL};
struct scan_field select_dtmcontrol = {
.in_value = NULL,
.out_value = ir_dtmcontrol
};
uint8_t ir_dbus[1] = {DBUS};
struct scan_field select_dbus = {
.in_value = NULL,
.out_value = ir_dbus
};
uint8_t ir_idcode[1] = {0x1};
struct scan_field select_idcode = {
.in_value = NULL,
.out_value = ir_idcode
};
struct trigger {
uint64_t address;
uint32_t length;
uint64_t mask;
uint64_t value;
bool read, write, execute;
int unique_id;
};
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
bool riscv_use_scratch_ram = false;
uint64_t riscv_scratch_ram_address = 0;
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{
struct scan_field field;
uint8_t in_value[4];
uint8_t out_value[4];
buf_set_u32(out_value, 0, 32, out);
jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
field.num_bits = 32;
field.out_value = out_value;
field.in_value = in_value;
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
/* Always return to dbus. */
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("failed jtag scan: %d", retval);
return retval;
}
uint32_t in = buf_get_u32(field.in_value, 0, 32);
LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
return in;
}
static struct target_type *get_target_type(struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
switch (info->dtm_version) {
case 0:
return &riscv011_target;
case 1:
return &riscv013_target;
default:
LOG_ERROR("Unsupported DTM version: %d", info->dtm_version);
return NULL;
}
}
static int riscv_init_target(struct command_context *cmd_ctx,
struct target *target)
{
LOG_DEBUG("riscv_init_target()");
target->arch_info = calloc(1, sizeof(riscv_info_t));
if (!target->arch_info)
return ERROR_FAIL;
riscv_info_t *info = (riscv_info_t *) target->arch_info;
riscv_info_init(target, info);
info->cmd_ctx = cmd_ctx;
select_dtmcontrol.num_bits = target->tap->ir_length;
select_dbus.num_bits = target->tap->ir_length;
select_idcode.num_bits = target->tap->ir_length;
return ERROR_OK;
}
static void riscv_deinit_target(struct target *target)
{
LOG_DEBUG("riscv_deinit_target()");
struct target_type *tt = get_target_type(target);
tt->deinit_target(target);
riscv_info_t *info = (riscv_info_t *) target->arch_info;
free(info);
target->arch_info = NULL;
}
static int oldriscv_halt(struct target *target)
{
struct target_type *tt = get_target_type(target);
return tt->halt(target);
}
static void trigger_from_breakpoint(struct trigger *trigger,
const struct breakpoint *breakpoint)
{
trigger->address = breakpoint->address;
trigger->length = breakpoint->length;
trigger->mask = ~0LL;
trigger->read = false;
trigger->write = false;
trigger->execute = true;
// unique_id is unique across both breakpoints and watchpoints.
trigger->unique_id = breakpoint->unique_id;
}
static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
struct trigger *trigger, uint64_t tdata1)
{
RISCV_INFO(r);
const uint32_t bpcontrol_x = 1<<0;
const uint32_t bpcontrol_w = 1<<1;
const uint32_t bpcontrol_r = 1<<2;
const uint32_t bpcontrol_u = 1<<3;
const uint32_t bpcontrol_s = 1<<4;
const uint32_t bpcontrol_h = 1<<5;
const uint32_t bpcontrol_m = 1<<6;
const uint32_t bpcontrol_bpmatch = 0xf << 7;
const uint32_t bpcontrol_bpaction = 0xff << 11;
if (tdata1 & (bpcontrol_r | bpcontrol_w | bpcontrol_x)) {
// Trigger is already in use, presumably by user code.
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
tdata1 = set_field(tdata1, bpcontrol_r, trigger->read);
tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
tdata1 = set_field(tdata1, bpcontrol_u, !!(r->misa & (1 << ('U' - 'A'))));
tdata1 = set_field(tdata1, bpcontrol_s, !!(r->misa & (1 << ('S' - 'A'))));
tdata1 = set_field(tdata1, bpcontrol_h, !!(r->misa & (1 << ('H' - 'A'))));
tdata1 |= bpcontrol_m;
tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); // exact match
tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); // cause bp exception
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
riscv_reg_t tdata1_rb = riscv_get_register_on_hart(target, hartid,
GDB_REGNO_TDATA1);
LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
if (tdata1 != tdata1_rb) {
LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
PRIx64 " to tdata1 it contains 0x%" PRIx64,
tdata1, tdata1_rb);
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
return ERROR_OK;
}
static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
struct trigger *trigger, uint64_t tdata1)
{
RISCV_INFO(r);
// tselect is already set
if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) {
// Trigger is already in use, presumably by user code.
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
// address/data match trigger
tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
tdata1 = set_field(tdata1, MCONTROL_ACTION,
MCONTROL_ACTION_DEBUG_MODE);
tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
tdata1 |= MCONTROL_M;
if (r->misa & (1 << ('H' - 'A')))
tdata1 |= MCONTROL_H;
if (r->misa & (1 << ('S' - 'A')))
tdata1 |= MCONTROL_S;
if (r->misa & (1 << ('U' - 'A')))
tdata1 |= MCONTROL_U;
if (trigger->execute)
tdata1 |= MCONTROL_EXECUTE;
if (trigger->read)
tdata1 |= MCONTROL_LOAD;
if (trigger->write)
tdata1 |= MCONTROL_STORE;
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
uint64_t tdata1_rb = riscv_get_register_on_hart(target, hartid, GDB_REGNO_TDATA1);
LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
if (tdata1 != tdata1_rb) {
LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
PRIx64 " to tdata1 it contains 0x%" PRIx64,
tdata1, tdata1_rb);
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
return ERROR_OK;
}
static int add_trigger(struct target *target, struct trigger *trigger)
{
RISCV_INFO(r);
// In RTOS mode, we need to set the same trigger in the same slot on every
// hart, to keep up the illusion that each hart is a thread running on the
// same core.
// Otherwise, we just set the trigger on the one hart this target deals
// with.
riscv_reg_t tselect[RISCV_MAX_HARTS];
int first_hart = -1;
for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid))
continue;
if (first_hart < 0)
first_hart = hartid;
tselect[hartid] = riscv_get_register_on_hart(target, hartid,
GDB_REGNO_TSELECT);
}
assert(first_hart >= 0);
unsigned int i;
for (i = 0; i < r->trigger_count[first_hart]; i++) {
if (r->trigger_unique_id[i] != -1) {
continue;
}
riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i);
uint64_t tdata1 = riscv_get_register_on_hart(target, first_hart, GDB_REGNO_TDATA1);
int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
int result = ERROR_OK;
for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
LOG_DEBUG(">>> hartid=%d", hartid);
if (!riscv_hart_enabled(target, hartid))
continue;
if (hartid > first_hart) {
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
}
switch (type) {
case 1:
result = maybe_add_trigger_t1(target, hartid, trigger, tdata1);
break;
case 2:
result = maybe_add_trigger_t2(target, hartid, trigger, tdata1);
break;
default:
LOG_DEBUG("trigger %d has unknown type %d", i, type);
continue;
}
if (result != ERROR_OK) {
continue;
}
}
if (result != ERROR_OK) {
continue;
}
LOG_DEBUG("Using trigger %d (type %d) for bp %d", i, type,
trigger->unique_id);
r->trigger_unique_id[i] = trigger->unique_id;
break;
}
for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid))
continue;
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT,
tselect[hartid]);
}
if (i >= r->trigger_count[first_hart]) {
LOG_ERROR("Couldn't find an available hardware trigger.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
return ERROR_OK;
}
int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
{
if (breakpoint->type == BKPT_SOFT) {
if (target_read_memory(target, breakpoint->address, breakpoint->length, 1,
breakpoint->orig_instr) != ERROR_OK) {
LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR,
breakpoint->address);
return ERROR_FAIL;
}
int retval;
if (breakpoint->length == 4) {
retval = target_write_u32(target, breakpoint->address, ebreak());
} else {
retval = target_write_u16(target, breakpoint->address, ebreak_c());
}
if (retval != ERROR_OK) {
LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%"
TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
return ERROR_FAIL;
}
} else if (breakpoint->type == BKPT_HARD) {
struct trigger trigger;
trigger_from_breakpoint(&trigger, breakpoint);
int result = add_trigger(target, &trigger);
if (result != ERROR_OK) {
return result;
}
} else {
LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
breakpoint->set = true;
return ERROR_OK;
}
static int remove_trigger(struct target *target, struct trigger *trigger)
{
RISCV_INFO(r);
int first_hart = -1;
for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid))
continue;
if (first_hart < 0) {
first_hart = hartid;
break;
}
}
assert(first_hart >= 0);
unsigned int i;
for (i = 0; i < r->trigger_count[first_hart]; i++) {
if (r->trigger_unique_id[i] == trigger->unique_id) {
break;
}
}
if (i >= r->trigger_count[first_hart]) {
LOG_ERROR("Couldn't find the hardware resources used by hardware "
"trigger.");
return ERROR_FAIL;
}
LOG_DEBUG("Stop using resource %d for bp %d", i, trigger->unique_id);
for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid))
continue;
riscv_reg_t tselect = riscv_get_register_on_hart(target, hartid, GDB_REGNO_TSELECT);
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
}
r->trigger_unique_id[i] = -1;
return ERROR_OK;
}
int riscv_remove_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if (breakpoint->type == BKPT_SOFT) {
if (target_write_memory(target, breakpoint->address, breakpoint->length, 1,
breakpoint->orig_instr) != ERROR_OK) {
LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
"0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
return ERROR_FAIL;
}
} else if (breakpoint->type == BKPT_HARD) {
struct trigger trigger;
trigger_from_breakpoint(&trigger, breakpoint);
int result = remove_trigger(target, &trigger);
if (result != ERROR_OK) {
return result;
}
} else {
LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
breakpoint->set = false;
return ERROR_OK;
}
static void trigger_from_watchpoint(struct trigger *trigger,
const struct watchpoint *watchpoint)
{
trigger->address = watchpoint->address;
trigger->length = watchpoint->length;
trigger->mask = watchpoint->mask;
trigger->value = watchpoint->value;
trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
trigger->execute = false;
// unique_id is unique across both breakpoints and watchpoints.
trigger->unique_id = watchpoint->unique_id;
}
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
{
struct trigger trigger;
trigger_from_watchpoint(&trigger, watchpoint);
int result = add_trigger(target, &trigger);
if (result != ERROR_OK) {
return result;
}
watchpoint->set = true;
return ERROR_OK;
}
int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint)
{
struct trigger trigger;
trigger_from_watchpoint(&trigger, watchpoint);
int result = remove_trigger(target, &trigger);
if (result != ERROR_OK) {
return result;
}
watchpoint->set = false;
return ERROR_OK;
}
static int oldriscv_step(struct target *target, int current, uint32_t address,
int handle_breakpoints)
{
struct target_type *tt = get_target_type(target);
return tt->step(target, current, address, handle_breakpoints);
}
static int old_or_new_riscv_step(
struct target *target,
int current,
target_addr_t address,
int handle_breakpoints
){
RISCV_INFO(r);
if (r->is_halted == NULL)
return oldriscv_step(target, current, address, handle_breakpoints);
else
return riscv_openocd_step(target, current, address, handle_breakpoints);
}
static int riscv_examine(struct target *target)
{
LOG_DEBUG("riscv_examine()");
if (target_was_examined(target)) {
LOG_DEBUG("Target was already examined.");
return ERROR_OK;
}
// Don't need to select dbus, since the first thing we do is read dtmcontrol.
riscv_info_t *info = (riscv_info_t *) target->arch_info;
uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
LOG_DEBUG(" version=0x%x", info->dtm_version);
struct target_type *tt = get_target_type(target);
if (tt == NULL)
return ERROR_FAIL;
int result = tt->init_target(info->cmd_ctx, target);
if (result != ERROR_OK)
return result;
return tt->examine(target);
}
static int oldriscv_poll(struct target *target)
{
struct target_type *tt = get_target_type(target);
return tt->poll(target);
}
static int old_or_new_riscv_poll(struct target *target)
{
RISCV_INFO(r);
if (r->is_halted == NULL)
return oldriscv_poll(target);
else
return riscv_openocd_poll(target);
}
static int old_or_new_riscv_halt(struct target *target)
{
RISCV_INFO(r);
if (r->is_halted == NULL)
return oldriscv_halt(target);
else
return riscv_openocd_halt(target);
}
static int riscv_assert_reset(struct target *target)
{
struct target_type *tt = get_target_type(target);
return tt->assert_reset(target);
}
static int riscv_deassert_reset(struct target *target)
{
LOG_DEBUG("RISCV DEASSERT RESET");
struct target_type *tt = get_target_type(target);
return tt->deassert_reset(target);
}
static int oldriscv_resume(struct target *target, int current, uint32_t address,
int handle_breakpoints, int debug_execution)
{
struct target_type *tt = get_target_type(target);
return tt->resume(target, current, address, handle_breakpoints,
debug_execution);
}
static int old_or_new_riscv_resume(
struct target *target,
int current,
target_addr_t address,
int handle_breakpoints,
int debug_execution
){
RISCV_INFO(r);
if (r->is_halted == NULL)
return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution);
else
return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution);
}
static int riscv_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
struct target_type *tt = get_target_type(target);
return tt->read_memory(target, address, size, count, buffer);
}
static int riscv_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
struct target_type *tt = get_target_type(target);
return tt->write_memory(target, address, size, count, buffer);
}
static int riscv_get_gdb_reg_list(struct target *target,
struct reg **reg_list[], int *reg_list_size,
enum target_register_class reg_class)
{
RISCV_INFO(r);
LOG_DEBUG("reg_class=%d", reg_class);
LOG_DEBUG("rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid);
if (r->rtos_hartid != -1 && riscv_rtos_enabled(target))
riscv_set_current_hartid(target, r->rtos_hartid);
else
riscv_set_current_hartid(target, target->coreid);
switch (reg_class) {
case REG_CLASS_GENERAL:
*reg_list_size = 32;
break;
case REG_CLASS_ALL:
*reg_list_size = REG_COUNT;
break;
default:
LOG_ERROR("Unsupported reg_class: %d", reg_class);
return ERROR_FAIL;
}
*reg_list = calloc(*reg_list_size, sizeof(struct reg *));
if (!*reg_list) {
return ERROR_FAIL;
}
if (!target->reg_cache) {
LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
return ERROR_FAIL;
}
for (int i = 0; i < *reg_list_size; i++) {
assert(target->reg_cache->reg_list[i].size > 0);
(*reg_list)[i] = &target->reg_cache->reg_list[i];
}
return ERROR_OK;
}
static int riscv_arch_state(struct target *target)
{
struct target_type *tt = get_target_type(target);
return tt->arch_state(target);
}
// Algorithm must end with a software breakpoint instruction.
static int riscv_run_algorithm(struct target *target, int num_mem_params,
struct mem_param *mem_params, int num_reg_params,
struct reg_param *reg_params, target_addr_t entry_point,
target_addr_t exit_point, int timeout_ms, void *arch_info)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
if (num_mem_params > 0) {
LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
return ERROR_FAIL;
}
if (target->state != TARGET_HALTED) {
LOG_WARNING("target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/// Save registers
struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", 1);
if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK) {
return ERROR_FAIL;
}
uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
uint64_t saved_regs[32];
for (int i = 0; i < num_reg_params; i++) {
LOG_DEBUG("save %s", reg_params[i].reg_name);
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
if (!r) {
LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name);
return ERROR_FAIL;
}
if (r->size != reg_params[i].size) {
LOG_ERROR("Register %s is %d bits instead of %d bits.",
reg_params[i].reg_name, r->size, reg_params[i].size);
return ERROR_FAIL;
}
if (r->number > REG_XPR31) {
LOG_ERROR("Only GPRs can be use as argument registers.");
return ERROR_FAIL;
}
if (r->type->get(r) != ERROR_OK) {
return ERROR_FAIL;
}
saved_regs[r->number] = buf_get_u64(r->value, 0, r->size);
if (r->type->set(r, reg_params[i].value) != ERROR_OK) {
return ERROR_FAIL;
}
}
// Disable Interrupts before attempting to run the algorithm.
uint64_t current_mstatus;
uint8_t mstatus_bytes[8];
LOG_DEBUG("Disabling Interrupts");
char mstatus_name[20];
sprintf(mstatus_name, "csr%d", CSR_MSTATUS);
struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
mstatus_name, 1);
reg_mstatus->type->get(reg_mstatus);
current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus,
ie_mask, 0));
reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
/// Run algorithm
LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point);
if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK) {
return ERROR_FAIL;
}
int64_t start = timeval_ms();
while (target->state != TARGET_HALTED) {
LOG_DEBUG("poll()");
int64_t now = timeval_ms();
if (now - start > timeout_ms) {
LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
LOG_ERROR(" now = 0x%08x", (uint32_t) now);
LOG_ERROR(" start = 0x%08x", (uint32_t) start);
oldriscv_halt(target);
old_or_new_riscv_poll(target);
return ERROR_TARGET_TIMEOUT;
}
int result = old_or_new_riscv_poll(target);
if (result != ERROR_OK) {
return result;
}
}
if (reg_pc->type->get(reg_pc) != ERROR_OK) {
return ERROR_FAIL;
}
uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
if (final_pc != exit_point) {
LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%"
TARGET_PRIxADDR, final_pc, exit_point);
return ERROR_FAIL;
}
// Restore Interrupts
LOG_DEBUG("Restoring Interrupts");
buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus);
reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
/// Restore registers
uint8_t buf[8];
buf_set_u64(buf, 0, info->xlen[0], saved_pc);
if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) {
return ERROR_FAIL;
}
for (int i = 0; i < num_reg_params; i++) {
LOG_DEBUG("restore %s", reg_params[i].reg_name);
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]);
if (r->type->set(r, buf) != ERROR_OK) {
return ERROR_FAIL;
}
}
return ERROR_OK;
}
/* Should run code on the target to perform CRC of
memory. Not yet implemented.
*/
static int riscv_checksum_memory(struct target *target,
target_addr_t address, uint32_t count,
uint32_t* checksum)
{
*checksum = 0xFFFFFFFF;
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
/* Should run code on the target to check whether a memory
block holds all-ones (because this is generally called on
NOR flash which is 1 when "blank")
Not yet implemented.
*/
int riscv_blank_check_memory(struct target * target,
target_addr_t address,
uint32_t count,
uint32_t * blank,
uint8_t erased_value)
{
*blank = 0;
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
/*** OpenOCD Helper Functions ***/
/* 0 means nothing happened, 1 means the hart's state changed (and thus the
* poll should terminate), and -1 means there was an error. */
static int riscv_poll_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
riscv_set_current_hartid(target, hartid);
LOG_DEBUG("polling hart %d, target->state=%d (TARGET_HALTED=%d)", hartid, target->state, TARGET_HALTED);
/* If OpenOCD this we're running but this hart is halted then it's time
* to raise an event. */
if (target->state != TARGET_HALTED && riscv_is_halted(target)) {
LOG_DEBUG(" triggered a halt");
r->on_halt(target);
return 1;
}
return 0;
}
/*** OpenOCD Interface ***/
int riscv_openocd_poll(struct target *target)
{
LOG_DEBUG("polling all harts");
int triggered_hart = -1;
if (riscv_rtos_enabled(target)) {
/* Check every hart for an event. */
for (int i = 0; i < riscv_count_harts(target); ++i) {
int out = riscv_poll_hart(target, i);
switch (out) {
case 0:
continue;
case 1:
triggered_hart = i;
break;
case -1:
return ERROR_FAIL;
}
}
if (triggered_hart == -1) {
LOG_DEBUG(" no harts just halted, target->state=%d", target->state);
return ERROR_OK;
}
LOG_DEBUG(" hart %d halted", triggered_hart);
/* If we're here then at least one hart triggered. That means
* we want to go and halt _every_ hart in the system, as that's
* the invariant we hold here. Some harts might have already
* halted (as we're either in single-step mode or they also
* triggered a breakpoint), so don't attempt to halt those
* harts. */
for (int i = 0; i < riscv_count_harts(target); ++i)
riscv_halt_one_hart(target, i);
} else {
if (riscv_poll_hart(target, riscv_current_hartid(target)) == 0)
return ERROR_OK;
triggered_hart = riscv_current_hartid(target);
LOG_DEBUG(" hart %d halted", triggered_hart);
}
target->state = TARGET_HALTED;
switch (riscv_halt_reason(target, triggered_hart)) {
case RISCV_HALT_BREAKPOINT:
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
case RISCV_HALT_INTERRUPT:
target->debug_reason = DBG_REASON_DBGRQ;
break;
case RISCV_HALT_SINGLESTEP:
target->debug_reason = DBG_REASON_SINGLESTEP;
break;
}
if (riscv_rtos_enabled(target)) {
target->rtos->current_threadid = triggered_hart + 1;
target->rtos->current_thread = triggered_hart + 1;
}
target->state = TARGET_HALTED;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return ERROR_OK;
}
int riscv_openocd_halt(struct target *target)
{
RISCV_INFO(r);
LOG_DEBUG("halting all harts");
int out = riscv_halt_all_harts(target);
if (out != ERROR_OK) {
LOG_ERROR("Unable to halt all harts");
return out;
}
register_cache_invalidate(target->reg_cache);
if (riscv_rtos_enabled(target)) {
target->rtos->current_threadid = r->rtos_hartid + 1;
target->rtos->current_thread = r->rtos_hartid + 1;
}
target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_DBGRQ;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return out;
}
int riscv_openocd_resume(
struct target *target,
int current,
target_addr_t address,
int handle_breakpoints,
int debug_execution
) {
LOG_DEBUG("resuming all harts");
if (!current) {
riscv_set_register(target, GDB_REGNO_PC, address);
}
int out = riscv_resume_all_harts(target);
if (out != ERROR_OK) {
LOG_ERROR("unable to resume all harts");
return out;
}
register_cache_invalidate(target->reg_cache);
target->state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
return out;
}
int riscv_openocd_step(
struct target *target,
int current,
target_addr_t address,
int handle_breakpoints
) {
LOG_DEBUG("stepping rtos hart");
if (!current) {
riscv_set_register(target, GDB_REGNO_PC, address);
}
int out = riscv_step_rtos_hart(target);
if (out != ERROR_OK) {
LOG_ERROR("unable to step rtos hart");
return out;
}
register_cache_invalidate(target->reg_cache);
target->state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_SINGLESTEP;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return out;
}
/* Command Handlers */
COMMAND_HANDLER(riscv_set_command_timeout_sec)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
int timeout = atoi(CMD_ARGV[0]);
if (timeout <= 0){
LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]);
return ERROR_FAIL;
}
riscv_command_timeout_sec = timeout;
return ERROR_OK;
}
COMMAND_HANDLER(riscv_set_reset_timeout_sec)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
int timeout = atoi(CMD_ARGV[0]);
if (timeout <= 0){
LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]);
return ERROR_FAIL;
}
riscv_reset_timeout_sec = timeout;
return ERROR_OK;
}
COMMAND_HANDLER(riscv_set_scratch_ram)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (!strcmp(CMD_ARGV[0], "none")) {
riscv_use_scratch_ram = false;
return ERROR_OK;
}
long long unsigned int address;
int result = sscanf(CMD_ARGV[0], "%Lx", &address);
if (result != (int) strlen(CMD_ARGV[0])) {
LOG_ERROR("%s is not a valid address for command.", CMD_ARGV[0]);
riscv_use_scratch_ram = false;
return ERROR_FAIL;
}
riscv_scratch_ram_address = address;
riscv_use_scratch_ram = true;
return ERROR_OK;
}
static const struct command_registration riscv_exec_command_handlers[] = {
{
.name = "set_command_timeout_sec",
.handler = riscv_set_command_timeout_sec,
.mode = COMMAND_ANY,
.usage = "riscv set_command_timeout_sec [sec]",
.help = "Set the wall-clock timeout (in seconds) for individual commands"
},
{
.name = "set_reset_timeout_sec",
.handler = riscv_set_reset_timeout_sec,
.mode = COMMAND_ANY,
.usage = "riscv set_reset_timeout_sec [sec]",
.help = "Set the wall-clock timeout (in seconds) after reset is deasserted"
},
{
.name = "set_scratch_ram",
.handler = riscv_set_scratch_ram,
.mode = COMMAND_ANY,
.usage = "riscv set_scratch_ram none|[address]",
.help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'."
},
COMMAND_REGISTRATION_DONE
};
const struct command_registration riscv_command_handlers[] = {
{
.name = "riscv",
.mode = COMMAND_ANY,
.help = "RISC-V Command Group",
.usage = "",
.chain = riscv_exec_command_handlers
},
COMMAND_REGISTRATION_DONE
};
struct target_type riscv_target =
{
.name = "riscv",
.init_target = riscv_init_target,
.deinit_target = riscv_deinit_target,
.examine = riscv_examine,
/* poll current target status */
.poll = old_or_new_riscv_poll,
.halt = old_or_new_riscv_halt,
.resume = old_or_new_riscv_resume,
.step = old_or_new_riscv_step,
.assert_reset = riscv_assert_reset,
.deassert_reset = riscv_deassert_reset,
.read_memory = riscv_read_memory,
.write_memory = riscv_write_memory,
.blank_check_memory = riscv_blank_check_memory,
.checksum_memory = riscv_checksum_memory,
.get_gdb_reg_list = riscv_get_gdb_reg_list,
.add_breakpoint = riscv_add_breakpoint,
.remove_breakpoint = riscv_remove_breakpoint,
.add_watchpoint = riscv_add_watchpoint,
.remove_watchpoint = riscv_remove_watchpoint,
.arch_state = riscv_arch_state,
.run_algorithm = riscv_run_algorithm,
.commands = riscv_command_handlers
};
/*** RISC-V Interface ***/
void riscv_info_init(struct target *target, riscv_info_t *r)
{
memset(r, 0, sizeof(*r));
r->dtm_version = 1;
r->registers_initialized = false;
r->current_hartid = target->coreid;
memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
r->xlen[h] = -1;
for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
r->valid_saved_registers[h][e] = false;
}
}
int riscv_halt_all_harts(struct target *target)
{
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (!riscv_hart_enabled(target, i))
continue;
riscv_halt_one_hart(target, i);
}
return ERROR_OK;
}
int riscv_halt_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("halting hart %d", hartid);
riscv_set_current_hartid(target, hartid);
if (riscv_is_halted(target)) {
LOG_DEBUG(" hart %d requested halt, but was already halted", hartid);
return ERROR_OK;
}
r->halt_current_hart(target);
return ERROR_OK;
}
int riscv_resume_all_harts(struct target *target)
{
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (!riscv_hart_enabled(target, i))
continue;
riscv_resume_one_hart(target, i);
}
riscv_invalidate_register_cache(target);
return ERROR_OK;
}
int riscv_resume_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("resuming hart %d", hartid);
riscv_set_current_hartid(target, hartid);
if (!riscv_is_halted(target)) {
LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid);
return ERROR_OK;
}
r->on_resume(target);
r->resume_current_hart(target);
return ERROR_OK;
}
int riscv_step_rtos_hart(struct target *target)
{
RISCV_INFO(r);
int hartid = r->current_hartid;
if (riscv_rtos_enabled(target)) {
hartid = r->rtos_hartid;
if (hartid == -1) {
LOG_USER("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
hartid = 0;
}
}
riscv_set_current_hartid(target, hartid);
LOG_DEBUG("stepping hart %d", hartid);
assert(riscv_is_halted(target));
riscv_invalidate_register_cache(target);
r->on_step(target);
r->step_current_hart(target);
riscv_invalidate_register_cache(target);
r->on_halt(target);
assert(riscv_is_halted(target));
return ERROR_OK;
}
int riscv_xlen(const struct target *target)
{
return riscv_xlen_of_hart(target, riscv_current_hartid(target));
}
int riscv_xlen_of_hart(const struct target *target, int hartid)
{
RISCV_INFO(r);
assert(r->xlen[hartid] != -1);
return r->xlen[hartid];
}
bool riscv_rtos_enabled(const struct target *target)
{
return target->rtos != NULL;
}
void riscv_set_current_hartid(struct target *target, int hartid)
{
RISCV_INFO(r);
if (!r->select_current_hart)
return;
int previous_hartid = riscv_current_hartid(target);
r->current_hartid = hartid;
assert(riscv_hart_enabled(target, hartid));
LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
r->select_current_hart(target);
/* This might get called during init, in which case we shouldn't be
* setting up the register cache. */
if (!target_was_examined(target))
return;
/* Avoid invalidating the register cache all the time. */
if (r->registers_initialized
&& (!riscv_rtos_enabled(target) || (previous_hartid == hartid))
&& target->reg_cache->reg_list[GDB_REGNO_XPR0].size == (unsigned)riscv_xlen(target)
&& (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) {
return;
} else
LOG_DEBUG("Initializing registers: xlen=%d", riscv_xlen(target));
riscv_invalidate_register_cache(target);
}
void riscv_invalidate_register_cache(struct target *target)
{
RISCV_INFO(r);
/* Update the register list's widths. */
register_cache_invalidate(target->reg_cache);
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
struct reg *reg = &target->reg_cache->reg_list[i];
reg->value = &r->reg_cache_values[i];
reg->valid = false;
switch (i) {
case GDB_REGNO_PRIV:
reg->size = 8;
break;
default:
reg->size = riscv_xlen(target);
break;
}
}
r->registers_initialized = true;
}
int riscv_current_hartid(const struct target *target)
{
RISCV_INFO(r);
return r->current_hartid;
}
void riscv_set_all_rtos_harts(struct target *target)
{
RISCV_INFO(r);
r->rtos_hartid = -1;
}
void riscv_set_rtos_hartid(struct target *target, int hartid)
{
LOG_DEBUG("setting RTOS hartid %d", hartid);
RISCV_INFO(r);
r->rtos_hartid = hartid;
}
int riscv_count_harts(struct target *target)
{
if (target == NULL) return 1;
RISCV_INFO(r);
if (r == NULL) return 1;
return r->hart_count;
}
bool riscv_has_register(struct target *target, int hartid, int regid)
{
return 1;
}
void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
{
return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
}
void riscv_set_register_on_hart(struct target *target, int hartid,
enum gdb_regno regid, uint64_t value)
{
RISCV_INFO(r);
LOG_DEBUG("[%d] %s <- %" PRIx64, hartid, gdb_regno_name(regid), value);
assert(r->set_register);
return r->set_register(target, hartid, regid, value);
}
riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno r)
{
return riscv_get_register_on_hart(target, riscv_current_hartid(target), r);
}
uint64_t riscv_get_register_on_hart(struct target *target, int hartid, enum gdb_regno regid)
{
RISCV_INFO(r);
uint64_t value = r->get_register(target, hartid, regid);
LOG_DEBUG("[%d] %s: %" PRIx64, hartid, gdb_regno_name(regid), value);
return value;
}
bool riscv_is_halted(struct target *target)
{
RISCV_INFO(r);
assert(r->is_halted);
return r->is_halted(target);
}
enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
{
RISCV_INFO(r);
riscv_set_current_hartid(target, hartid);
assert(riscv_is_halted(target));
return r->halt_reason(target);
}
int riscv_count_triggers(struct target *target)
{
return riscv_count_triggers_of_hart(target, riscv_current_hartid(target));
}
int riscv_count_triggers_of_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
assert(hartid < riscv_count_harts(target));
return r->trigger_count[hartid];
}
size_t riscv_debug_buffer_size(struct target *target)
{
RISCV_INFO(r);
return r->debug_buffer_size[riscv_current_hartid(target)];
}
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
{
RISCV_INFO(r);
r->write_debug_buffer(target, index, insn);
return ERROR_OK;
}
riscv_insn_t riscv_read_debug_buffer(struct target *target, int index)
{
RISCV_INFO(r);
return r->read_debug_buffer(target, index);
}
riscv_addr_t riscv_read_debug_buffer_x(struct target *target, int index)
{
riscv_addr_t out = 0;
switch (riscv_xlen(target)) {
case 64:
out |= (uint64_t)riscv_read_debug_buffer(target, index + 1) << 32;
case 32:
out |= riscv_read_debug_buffer(target, index + 0) << 0;
break;
default:
LOG_ERROR("unsupported XLEN %d", riscv_xlen(target));
abort();
}
return out;
}
int riscv_execute_debug_buffer(struct target *target)
{
RISCV_INFO(r);
return r->execute_debug_buffer(target);
}
void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
{
RISCV_INFO(r);
r->fill_dmi_write_u64(target, buf, a, d);
}
void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a)
{
RISCV_INFO(r);
r->fill_dmi_read_u64(target, buf, a);
}
void riscv_fill_dmi_nop_u64(struct target *target, char *buf)
{
RISCV_INFO(r);
r->fill_dmi_nop_u64(target, buf);
}
int riscv_dmi_write_u64_bits(struct target *target)
{
RISCV_INFO(r);
return r->dmi_write_u64_bits(target);
}
bool riscv_hart_enabled(struct target *target, int hartid)
{
/* FIXME: Add a hart mask to the RTOS. */
if (riscv_rtos_enabled(target))
return hartid < riscv_count_harts(target);
return hartid == target->coreid;
}
/**
* Count triggers, and initialize trigger_count for each hart.
* trigger_count is initialized even if this function fails to discover
* something.
* Disable any hardware triggers that have dmode set. We can't have set them
* ourselves. Maybe they're left over from some killed debug session.
* */
int riscv_enumerate_triggers(struct target *target)
{
RISCV_INFO(r);
for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid))
continue;
riscv_reg_t tselect = riscv_get_register_on_hart(target, hartid,
GDB_REGNO_TSELECT);
for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
r->trigger_count[hartid] = t;
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t);
uint64_t tselect_rb = riscv_get_register_on_hart(target, hartid,
GDB_REGNO_TSELECT);
// Mask off the top bit, which is used as tdrmode in old
// implementations.
tselect_rb &= ~(1ULL << (riscv_xlen(target)-1));
if (tselect_rb != t)
break;
uint64_t tdata1 = riscv_get_register_on_hart(target, hartid,
GDB_REGNO_TDATA1);
int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
switch (type) {
case 1:
// On these older cores we don't support software using
// triggers.
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
break;
case 2:
if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) {
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
}
break;
}
}
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
LOG_INFO("[%d] Found %d triggers", hartid, r->trigger_count[hartid]);
}
return ERROR_OK;
}
const char *gdb_regno_name(enum gdb_regno regno)
{
static char buf[32];
switch (regno) {
case GDB_REGNO_ZERO:
return "zero";
case GDB_REGNO_S0:
return "s0";
case GDB_REGNO_S1:
return "s1";
case GDB_REGNO_PC:
return "pc";
case GDB_REGNO_FPR0:
return "fpr0";
case GDB_REGNO_FPR31:
return "fpr31";
case GDB_REGNO_CSR0:
return "csr0";
case GDB_REGNO_TSELECT:
return "tselect";
case GDB_REGNO_TDATA1:
return "tdata1";
case GDB_REGNO_TDATA2:
return "tdata2";
case GDB_REGNO_MISA:
return "misa";
case GDB_REGNO_DPC:
return "dpc";
case GDB_REGNO_DCSR:
return "dcsr";
case GDB_REGNO_DSCRATCH:
return "dscratch";
case GDB_REGNO_MSTATUS:
return "mstatus";
case GDB_REGNO_PRIV:
return "priv";
default:
if (regno <= GDB_REGNO_XPR31) {
sprintf(buf, "x%d", regno - GDB_REGNO_XPR0);
} else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0);
} else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) {
sprintf(buf, "f%d", regno - GDB_REGNO_FPR0);
} else {
sprintf(buf, "gdb_regno_%d", regno);
}
return buf;
}
}