Add a RISC-V RTOS, which natievly supports multiple harts
This is a work in progress, but it's at the point where you can actually debug multi-hart programs now.
This commit is contained in:
parent
7203102c25
commit
d0fcbc9b51
|
@ -20,8 +20,8 @@ include $(top_srcdir)/common.mk
|
|||
|
||||
METASOURCES = AUTO
|
||||
noinst_LTLIBRARIES = librtos.la
|
||||
noinst_HEADERS = rtos.h rtos_standard_stackings.h rtos_ecos_stackings.h linux_header.h rtos_chibios_stackings.h rtos_embkernel_stackings.h rtos_mqx_stackings.h
|
||||
librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c rtos_chibios_stackings.c rtos_embkernel_stackings.c rtos_mqx_stackings.c FreeRTOS.c ThreadX.c eCos.c linux.c ChibiOS.c embKernel.c mqx.c
|
||||
noinst_HEADERS = rtos.h rtos_standard_stackings.h rtos_ecos_stackings.h linux_header.h rtos_chibios_stackings.h rtos_embkernel_stackings.h rtos_mqx_stackings.h riscv_debug.h
|
||||
librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c rtos_chibios_stackings.c rtos_embkernel_stackings.c rtos_mqx_stackings.c FreeRTOS.c ThreadX.c eCos.c linux.c ChibiOS.c embKernel.c mqx.c riscv_debug.c
|
||||
|
||||
librtos_la_CFLAGS =
|
||||
if IS_MINGW
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "riscv_debug.h"
|
||||
#include "target/target.h"
|
||||
#include "target/riscv/riscv.h"
|
||||
#include "rtos.h"
|
||||
#include "server/gdb_server.h"
|
||||
|
||||
static int riscv_update_threads(struct rtos *rtos);
|
||||
static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size);
|
||||
|
||||
static int riscv_detect_rtos(struct target *target)
|
||||
{
|
||||
LOG_ERROR("riscv_detect_rtos() unimplemented");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int riscv_create_rtos(struct target *target)
|
||||
{
|
||||
LOG_DEBUG("RISC-V Debug 'RTOS' created: this doesn't meat you're running an RTOS, just that you have multi-hart support on RISC-V");
|
||||
|
||||
struct riscv_rtos *r = calloc(1, sizeof(*r));
|
||||
target->rtos->rtos_specific_params = r;
|
||||
#if 0
|
||||
r->target_hartid = 0;
|
||||
r->target_any_hart = true;
|
||||
r->target_every_hart = true;
|
||||
#endif
|
||||
|
||||
target->rtos->current_threadid = 1;
|
||||
target->rtos->current_thread = 1;
|
||||
riscv_update_threads(target->rtos);
|
||||
|
||||
target->rtos->gdb_thread_packet = riscv_gdb_thread_packet;
|
||||
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
static int riscv_update_threads(struct rtos *rtos)
|
||||
{
|
||||
LOG_DEBUG("Updating the RISC-V Hart List");
|
||||
|
||||
/* Figures out how many harts there are on the system. */
|
||||
int hart_count = riscv_count_harts(rtos->target);
|
||||
if (rtos->thread_count != hart_count) {
|
||||
rtos_free_threadlist(rtos);
|
||||
rtos->thread_count = hart_count;
|
||||
rtos->thread_details = calloc(rtos->thread_count, sizeof(*rtos->thread_details));
|
||||
for (int i = 0; i < rtos->thread_count; ++i) {
|
||||
LOG_DEBUG(" Setting up Hart %d", i);
|
||||
rtos->thread_details[i].threadid = i + 1;
|
||||
rtos->thread_details[i].exists = true;
|
||||
if (asprintf(&rtos->thread_details[i].thread_name_str, "Hart %d", i) < 0)
|
||||
LOG_ERROR("riscv_update_threads() failed asprintf");
|
||||
if (asprintf(&rtos->thread_details[i].extra_info_str, "RV64") < 0)
|
||||
LOG_ERROR("riscv_update_threads() failed asprintf");
|
||||
}
|
||||
}
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size)
|
||||
{
|
||||
struct target *target = get_target_from_connection(connection);
|
||||
struct rtos *rtos = target->rtos;
|
||||
struct riscv_rtos *r = (struct riscv_rtos *)(target->rtos->rtos_specific_params);
|
||||
|
||||
char *packet_stttrr = malloc(packet_size + 1);
|
||||
memset(packet_stttrr, '\0', packet_size + 1);
|
||||
memcpy(packet_stttrr, packet, packet_size);
|
||||
LOG_DEBUG("riscv_gdb_thread_packet(%s)", packet_stttrr);
|
||||
|
||||
switch (packet[0]) {
|
||||
case 'q':
|
||||
if (strncmp(packet, "qfThreadInfo", 12) == 0) {
|
||||
riscv_update_threads(target->rtos);
|
||||
r->qs_thread_info_offset = 1;
|
||||
|
||||
char m[16];
|
||||
snprintf(m, 16, "m%08x", (int)rtos->thread_details[0].threadid);
|
||||
gdb_put_packet(connection, m, strlen(m));
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (strncmp(packet, "qsThreadInfo", 12) == 0) {
|
||||
if (r->qs_thread_info_offset >= rtos->thread_count) {
|
||||
gdb_put_packet(connection, "l", 1);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int tid = r->qs_thread_info_offset++;
|
||||
char m[16];
|
||||
snprintf(m, 16, "m%08x", (int)rtos->thread_details[tid].threadid);
|
||||
gdb_put_packet(connection, m, strlen(m));
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (strncmp(packet, "qAttached", 9) == 0) {
|
||||
gdb_put_packet(connection, "1", 1);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (strncmp(packet, "qThreadExtraInfo", 16) == 0) {
|
||||
char tid_str[32];
|
||||
memcpy(tid_str, packet + 17, packet_size - 17);
|
||||
tid_str[packet_size - 17] = '\0';
|
||||
char *end;
|
||||
int tid = strtol(tid_str, &end, 16);
|
||||
if (*end != '\0') {
|
||||
LOG_ERROR("Got qThreadExtraInfo with non-numeric TID: '%s'", tid_str);
|
||||
gdb_put_packet(connection, NULL, 0);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
char m[16];
|
||||
snprintf(m, 16, "hart %d", tid);
|
||||
char h[33];
|
||||
h[0] = '\0';
|
||||
for (size_t i = 0; i < strlen(m); ++i) {
|
||||
char byte[3];
|
||||
snprintf(byte, 3, "%02x", m[i]);
|
||||
strncat(h, byte, 32);
|
||||
}
|
||||
gdb_put_packet(connection, h, strlen(h));
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
return GDB_THREAD_PACKET_NOT_CONSUMED;
|
||||
|
||||
case 'Q':
|
||||
return GDB_THREAD_PACKET_NOT_CONSUMED;
|
||||
|
||||
case 'H':
|
||||
/* ‘H op thread-id’
|
||||
*
|
||||
* Set thread for subsequent operations (‘m’, ‘M’, ‘g’, ‘G’,
|
||||
* et.al.). Depending on the operation to be performed, op
|
||||
* should be ‘c’ for step and continue operations (note that
|
||||
* this is deprecated, supporting the ‘vCont’ command is a
|
||||
* better option), and ‘g’ for other operations. The thread
|
||||
* designator thread-id has the format and interpretation
|
||||
* described in thread-id syntax.
|
||||
*
|
||||
* Reply:
|
||||
* ‘OK’ for success
|
||||
* ‘E NN’ for an error
|
||||
*/
|
||||
{
|
||||
char tid_str[32];
|
||||
memcpy(tid_str, packet + 2, packet_size - 2);
|
||||
tid_str[packet_size - 2] = '\0';
|
||||
char *entptr;
|
||||
int tid = strtol(tid_str, &entptr, 16);
|
||||
if (*entptr != '\0') {
|
||||
LOG_ERROR("Got H packet, but without integer: %s", tid_str);
|
||||
return GDB_THREAD_PACKET_NOT_CONSUMED;
|
||||
}
|
||||
|
||||
riscv_enable_rtos(target);
|
||||
switch (tid) {
|
||||
case 0:
|
||||
case -1:
|
||||
riscv_set_all_rtos_harts(target);
|
||||
break;
|
||||
default:
|
||||
riscv_set_rtos_hartid(target, tid - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (packet[1]) {
|
||||
case 'g':
|
||||
case 'c':
|
||||
gdb_put_packet(connection, "OK", 2);
|
||||
return ERROR_OK;
|
||||
default:
|
||||
LOG_ERROR("Unknown H packet subtype %2x\n", packet[1]);
|
||||
gdb_put_packet(connection, NULL, 0);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
case 'T':
|
||||
{
|
||||
char tid_str[32];
|
||||
memcpy(tid_str, packet + 1, packet_size - 1);
|
||||
tid_str[packet_size - 1] = '\0';
|
||||
char *end;
|
||||
int tid = strtol(tid_str, &end, 16);
|
||||
if (*end != '\0') {
|
||||
LOG_ERROR("T packet with non-numeric tid %s", tid_str);
|
||||
gdb_put_packet(connection, NULL, 0);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
riscv_enable_rtos(target);
|
||||
riscv_update_threads(target->rtos);
|
||||
if (tid <= target->rtos->thread_count) {
|
||||
gdb_put_packet(connection, "OK", 2);
|
||||
return ERROR_OK;
|
||||
} else {
|
||||
gdb_put_packet(connection, "E00", 3);
|
||||
return ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case 'c':
|
||||
case 's':
|
||||
target->state = TARGET_HALTED;
|
||||
return JIM_OK;
|
||||
|
||||
case 'R':
|
||||
{
|
||||
char *packet_str = malloc(packet_size + 1);
|
||||
memset(packet_str, '\0', packet_size + 1);
|
||||
memcpy(packet_str, packet, packet_size);
|
||||
LOG_WARNING("riscv_gdb_thread_packet(%s): unimplemented", packet_str);
|
||||
gdb_put_packet(connection, NULL, 0);
|
||||
return JIM_OK;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR("Unknown packet of type 0x%2.2x", packet[0]);
|
||||
gdb_put_packet(connection, NULL, 0);
|
||||
return JIM_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list)
|
||||
{
|
||||
LOG_DEBUG("Updating RISC-V regiser list for hart %d", (int)(thread_id - 1));
|
||||
|
||||
#if 0
|
||||
LOG_ERROR(" Not actually updating");
|
||||
*hex_reg_list = 0;
|
||||
return JIM_OK;
|
||||
#endif
|
||||
|
||||
size_t n_regs = 32;
|
||||
size_t xlen = 64;
|
||||
size_t reg_chars = xlen / 8 * 2;
|
||||
|
||||
ssize_t hex_reg_list_length = n_regs * reg_chars + 2;
|
||||
*hex_reg_list = malloc(hex_reg_list_length);
|
||||
*hex_reg_list[0] = '\0';
|
||||
for (size_t i = 0; i < n_regs; ++i) {
|
||||
if (riscv_has_register(rtos->target, thread_id, i)) {
|
||||
uint64_t reg_value = riscv_get_register(rtos->target, thread_id - 1, i);
|
||||
for (size_t byte = 0; byte < xlen / 8; ++byte) {
|
||||
uint8_t reg_byte = reg_value >> (byte * 8);
|
||||
char hex[3];
|
||||
snprintf(hex, 3, "%02x", reg_byte);
|
||||
strncat(*hex_reg_list, hex, hex_reg_list_length);
|
||||
}
|
||||
} else {
|
||||
for (size_t byte = 0; byte < xlen / 8; ++byte)
|
||||
strncat(*hex_reg_list, "xx", hex_reg_list_length);
|
||||
}
|
||||
}
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
static int riscv_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
|
||||
{
|
||||
*symbol_list = calloc(1, sizeof(symbol_table_elem_t));
|
||||
(*symbol_list)[0].symbol_name = NULL;
|
||||
(*symbol_list)[0].optional = false;
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
const struct rtos_type riscv_rtos =
|
||||
{
|
||||
.name = "riscv",
|
||||
.detect_rtos = riscv_detect_rtos,
|
||||
.create = riscv_create_rtos,
|
||||
.update_threads = riscv_update_threads,
|
||||
.get_thread_reg_list = riscv_get_thread_reg_list,
|
||||
.get_symbol_list_to_lookup = riscv_get_symbol_list_to_lookup,
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef RTOS__RISCV_H
|
||||
#define RTOS__RISCV_H
|
||||
|
||||
struct riscv_rtos {
|
||||
/* The index into the thread list used to handle */
|
||||
int qs_thread_info_offset;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -34,6 +34,7 @@ extern struct rtos_type Linux_os;
|
|||
extern struct rtos_type ChibiOS_rtos;
|
||||
extern struct rtos_type embKernel_rtos;
|
||||
extern struct rtos_type mqx_rtos;
|
||||
extern struct rtos_type riscv_rtos;
|
||||
|
||||
static struct rtos_type *rtos_types[] = {
|
||||
&ThreadX_rtos,
|
||||
|
@ -43,6 +44,7 @@ static struct rtos_type *rtos_types[] = {
|
|||
&ChibiOS_rtos,
|
||||
&embKernel_rtos,
|
||||
&mqx_rtos,
|
||||
&riscv_rtos,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -418,7 +420,7 @@ int rtos_get_gdb_reg_list(struct connection *connection)
|
|||
(target->smp))) { /* in smp several current thread are possible */
|
||||
char *hex_reg_list;
|
||||
|
||||
LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
|
||||
LOG_INFO("RTOS: getting register list for thread 0x%" PRIx64
|
||||
", target->rtos->current_thread=0x%" PRIx64 "\r\n",
|
||||
current_threadid,
|
||||
target->rtos->current_thread);
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef TARGET__RISCV__ASM_H
|
||||
#define TARGET__RISCV__ASM_H
|
||||
|
||||
#include "riscv.h"
|
||||
|
||||
/*** Version-independent functions that we don't want in the main address space. ***/
|
||||
|
||||
static uint32_t load(const struct target *target, unsigned int rd,
|
||||
unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||
static uint32_t load(const struct target *target, unsigned int rd,
|
||||
unsigned int base, uint16_t offset)
|
||||
{
|
||||
switch (riscv_xlen(target)) {
|
||||
case 32:
|
||||
return lw(rd, base, offset);
|
||||
case 64:
|
||||
return ld(rd, base, offset);
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static uint32_t store(const struct target *target, unsigned int src,
|
||||
unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||
static uint32_t store(const struct target *target, unsigned int src,
|
||||
unsigned int base, uint16_t offset)
|
||||
{
|
||||
switch (riscv_xlen(target)) {
|
||||
case 32:
|
||||
return sw(src, base, offset);
|
||||
case 64:
|
||||
return sd(src, base, offset);
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef TARGET__RISCV__GDB_REGS_H
|
||||
#define TARGET__RISCV__GDB_REGS_H
|
||||
|
||||
enum gdb_regno {
|
||||
GDB_REGNO_XPR0 = 0,
|
||||
GDB_REGNO_S0 = GDB_REGNO_XPR0 + 8,
|
||||
GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9,
|
||||
GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31,
|
||||
GDB_REGNO_PC = 32,
|
||||
GDB_REGNO_FPR0 = 33,
|
||||
GDB_REGNO_FPR31 = GDB_REGNO_FPR0 + 31,
|
||||
GDB_REGNO_CSR0 = 65,
|
||||
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
|
||||
GDB_REGNO_PRIV = 4161,
|
||||
GDB_REGNO_COUNT
|
||||
};
|
||||
|
||||
#endif
|
|
@ -271,3 +271,9 @@ static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
|
|||
(dest << 7) |
|
||||
MATCH_SRLI;
|
||||
}
|
||||
|
||||
static uint32_t fence(void) __attribute__((unused));
|
||||
static uint32_t fence(void)
|
||||
{
|
||||
return MATCH_FENCE;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "breakpoints.h"
|
||||
#include "helper/time_support.h"
|
||||
#include "riscv.h"
|
||||
#include "asm.h"
|
||||
|
||||
/**
|
||||
* Since almost everything can be accomplish by scanning the dbus register, all
|
||||
|
@ -260,7 +261,7 @@ static riscv011_info_t *get_info(const struct target *target)
|
|||
static unsigned int slot_offset(const struct target *target, slot_t slot)
|
||||
{
|
||||
riscv011_info_t *info = get_info(target);
|
||||
switch (xlen(target)) {
|
||||
switch (riscv_xlen(target)) {
|
||||
case 32:
|
||||
switch (slot) {
|
||||
case SLOT0: return 4;
|
||||
|
@ -275,7 +276,7 @@ static unsigned int slot_offset(const struct target *target, slot_t slot)
|
|||
}
|
||||
}
|
||||
LOG_ERROR("slot_offset called with xlen=%d, slot=%d",
|
||||
xlen(target), slot);
|
||||
riscv_xlen(target), slot);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
|
@ -537,8 +538,8 @@ static scans_t *scans_new(struct target *target, unsigned int scan_count)
|
|||
scans_t *scans = malloc(sizeof(scans_t));
|
||||
scans->scan_count = scan_count;
|
||||
// This code also gets called before xlen is detected.
|
||||
if (xlen(target))
|
||||
scans->scan_size = 2 + xlen(target) / 8;
|
||||
if (riscv_xlen(target))
|
||||
scans->scan_size = 2 + riscv_xlen(target) / 8;
|
||||
else
|
||||
scans->scan_size = 2 + 128 / 8;
|
||||
scans->next_scan = 0;
|
||||
|
@ -641,7 +642,7 @@ static void scans_add_read32(scans_t *scans, uint16_t address, bool set_interrup
|
|||
static void scans_add_read(scans_t *scans, slot_t slot, bool set_interrupt)
|
||||
{
|
||||
const struct target *target = scans->target;
|
||||
switch (xlen(target)) {
|
||||
switch (riscv_xlen(target)) {
|
||||
case 32:
|
||||
scans_add_read32(scans, slot_offset(target, slot), set_interrupt);
|
||||
break;
|
||||
|
@ -776,7 +777,7 @@ static void cache_set(struct target *target, slot_t slot, uint64_t data)
|
|||
{
|
||||
unsigned int offset = slot_offset(target, slot);
|
||||
cache_set32(target, offset, data);
|
||||
if (xlen(target) > 32) {
|
||||
if (riscv_xlen(target) > 32) {
|
||||
cache_set32(target, offset + 1, data >> 32);
|
||||
}
|
||||
}
|
||||
|
@ -996,7 +997,7 @@ static uint64_t cache_get(struct target *target, slot_t slot)
|
|||
{
|
||||
unsigned int offset = slot_offset(target, slot);
|
||||
uint64_t value = cache_get32(target, offset);
|
||||
if (xlen(target) > 32) {
|
||||
if (riscv_xlen(target) > 32) {
|
||||
value |= ((uint64_t) cache_get32(target, offset + 1)) << 32;
|
||||
}
|
||||
return value;
|
||||
|
@ -1117,7 +1118,7 @@ static int execute_resume(struct target *target, bool step)
|
|||
|
||||
struct reg *mstatus_reg = &target->reg_cache->reg_list[REG_MSTATUS];
|
||||
if (mstatus_reg->valid) {
|
||||
uint64_t mstatus_user = buf_get_u64(mstatus_reg->value, 0, xlen(target));
|
||||
uint64_t mstatus_user = buf_get_u64(mstatus_reg->value, 0, riscv_xlen(target));
|
||||
if (mstatus_user != info->mstatus_actual) {
|
||||
cache_set_load(target, 0, S0, SLOT0);
|
||||
cache_set32(target, 1, csrw(S0, CSR_MSTATUS));
|
||||
|
@ -1198,18 +1199,18 @@ static void update_reg_list(struct target *target)
|
|||
if (info->reg_values) {
|
||||
free(info->reg_values);
|
||||
}
|
||||
info->reg_values = malloc(REG_COUNT * xlen(target) / 4);
|
||||
info->reg_values = malloc(REG_COUNT * riscv_xlen(target) / 4);
|
||||
|
||||
for (unsigned int i = 0; i < REG_COUNT; i++) {
|
||||
struct reg *r = &target->reg_cache->reg_list[i];
|
||||
r->value = info->reg_values + i * xlen(target) / 4;
|
||||
r->value = info->reg_values + i * riscv_xlen(target) / 4;
|
||||
if (r->dirty) {
|
||||
LOG_ERROR("Register %d was dirty. Its value is lost.", i);
|
||||
}
|
||||
if (i == REG_PRIV) {
|
||||
r->size = 8;
|
||||
} else {
|
||||
r->size = xlen(target);
|
||||
r->size = riscv_xlen(target);
|
||||
}
|
||||
r->valid = false;
|
||||
}
|
||||
|
@ -1259,7 +1260,7 @@ static int register_get(struct reg *reg)
|
|||
maybe_write_tselect(target);
|
||||
|
||||
if (reg->number <= REG_XPR31) {
|
||||
buf_set_u64(reg->value, 0, xlen(target), reg_cache_get(target, reg->number));
|
||||
buf_set_u64(reg->value, 0, riscv_xlen(target), reg_cache_get(target, reg->number));
|
||||
LOG_DEBUG("%s=0x%" PRIx64, reg->name, reg_cache_get(target, reg->number));
|
||||
return ERROR_OK;
|
||||
} else if (reg->number == REG_PC) {
|
||||
|
@ -1280,7 +1281,7 @@ static int register_get(struct reg *reg)
|
|||
cache_set(target, SLOT1, info->mstatus_actual);
|
||||
}
|
||||
|
||||
if (xlen(target) == 32) {
|
||||
if (riscv_xlen(target) == 32) {
|
||||
cache_set32(target, i++, fsw(reg->number - REG_FPR0, 0, DEBUG_RAM_START + 16));
|
||||
} else {
|
||||
cache_set32(target, i++, fsd(reg->number - REG_FPR0, 0, DEBUG_RAM_START + 16));
|
||||
|
@ -1308,13 +1309,13 @@ static int register_get(struct reg *reg)
|
|||
if (exception) {
|
||||
LOG_ERROR("Got exception 0x%x when reading register %d", exception,
|
||||
reg->number);
|
||||
buf_set_u64(reg->value, 0, xlen(target), ~0);
|
||||
buf_set_u64(reg->value, 0, riscv_xlen(target), ~0);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
uint64_t value = cache_get(target, SLOT0);
|
||||
LOG_DEBUG("%s=0x%" PRIx64, reg->name, value);
|
||||
buf_set_u64(reg->value, 0, xlen(target), value);
|
||||
buf_set_u64(reg->value, 0, riscv_xlen(target), value);
|
||||
|
||||
if (reg->number == REG_MSTATUS) {
|
||||
info->mstatus_actual = value;
|
||||
|
@ -1358,7 +1359,7 @@ static int register_write(struct target *target, unsigned int number,
|
|||
cache_set(target, SLOT1, info->mstatus_actual);
|
||||
}
|
||||
|
||||
if (xlen(target) == 32) {
|
||||
if (riscv_xlen(target) == 32) {
|
||||
cache_set32(target, i++, flw(number - REG_FPR0, 0, DEBUG_RAM_START + 16));
|
||||
} else {
|
||||
cache_set32(target, i++, fld(number - REG_FPR0, 0, DEBUG_RAM_START + 16));
|
||||
|
@ -1399,7 +1400,7 @@ static int register_set(struct reg *reg, uint8_t *buf)
|
|||
{
|
||||
struct target *target = (struct target *) reg->arch_info;
|
||||
|
||||
uint64_t value = buf_get_u64(buf, 0, xlen(target));
|
||||
uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target));
|
||||
|
||||
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
|
||||
struct reg *r = &target->reg_cache->reg_list[reg->number];
|
||||
|
@ -1437,6 +1438,7 @@ static int init_target(struct command_context *cmd_ctx,
|
|||
{
|
||||
LOG_DEBUG("init");
|
||||
riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
|
||||
generic_info->get_register = NULL;
|
||||
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
|
||||
if (!generic_info->version_specific)
|
||||
return ERROR_FAIL;
|
||||
|
@ -1510,7 +1512,7 @@ static int add_trigger(struct target *target, struct trigger *trigger)
|
|||
|
||||
uint64_t tdata1;
|
||||
read_csr(target, &tdata1, CSR_TDATA1);
|
||||
int type = get_field(tdata1, MCONTROL_TYPE(xlen(target)));
|
||||
int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
|
||||
|
||||
if (type != 2) {
|
||||
continue;
|
||||
|
@ -1522,7 +1524,7 @@ static int add_trigger(struct target *target, struct trigger *trigger)
|
|||
}
|
||||
|
||||
// address/data match trigger
|
||||
tdata1 |= MCONTROL_DMODE(xlen(target));
|
||||
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);
|
||||
|
@ -1769,9 +1771,9 @@ static int step(struct target *target, int current, uint32_t address,
|
|||
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
|
||||
|
||||
if (!current) {
|
||||
if (xlen(target) > 32) {
|
||||
if (riscv_xlen(target) > 32) {
|
||||
LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
|
||||
xlen(target));
|
||||
riscv_xlen(target));
|
||||
}
|
||||
int result = register_write(target, REG_PC, address);
|
||||
if (result != ERROR_OK)
|
||||
|
@ -1875,11 +1877,11 @@ static int examine(struct target *target)
|
|||
uint32_t word1 = cache_get32(target, 1);
|
||||
riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
|
||||
if (word0 == 1 && word1 == 0) {
|
||||
generic_info->xlen = 32;
|
||||
generic_info->xlen[0] = 32;
|
||||
} else if (word0 == 0xffffffff && word1 == 3) {
|
||||
generic_info->xlen = 64;
|
||||
generic_info->xlen[0] = 64;
|
||||
} else if (word0 == 0xffffffff && word1 == 0xffffffff) {
|
||||
generic_info->xlen = 128;
|
||||
generic_info->xlen[0] = 128;
|
||||
} else {
|
||||
uint32_t exception = cache_get32(target, info->dramsize-1);
|
||||
LOG_ERROR("Failed to discover xlen; word0=0x%x, word1=0x%x, exception=0x%x",
|
||||
|
@ -1887,7 +1889,7 @@ static int examine(struct target *target)
|
|||
dump_debug_ram(target);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
LOG_DEBUG("Discovered XLEN is %d", xlen(target));
|
||||
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
|
||||
|
||||
// Update register list to match discovered XLEN.
|
||||
update_reg_list(target);
|
||||
|
@ -1905,7 +1907,7 @@ static int examine(struct target *target)
|
|||
}
|
||||
|
||||
target_set_examined(target);
|
||||
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, xlen(target), info->misa);
|
||||
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, riscv_xlen(target), info->misa);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -2030,10 +2032,10 @@ static riscv_error_t handle_halt_routine(struct target *target)
|
|||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (xlen(target) == 32) {
|
||||
if (riscv_xlen(target) == 32) {
|
||||
reg_cache_set(target, reg, data & 0xffffffff);
|
||||
result++;
|
||||
} else if (xlen(target) == 64) {
|
||||
} else if (riscv_xlen(target) == 64) {
|
||||
if (address == 4) {
|
||||
value = data & 0xffffffff;
|
||||
} else if (address == 5) {
|
||||
|
@ -2125,7 +2127,7 @@ static int handle_halt(struct target *target, bool announce)
|
|||
break;
|
||||
uint64_t tdata1;
|
||||
read_csr(target, &tdata1, CSR_TDATA1);
|
||||
if ((tdata1 & MCONTROL_DMODE(xlen(target))) &&
|
||||
if ((tdata1 & MCONTROL_DMODE(riscv_xlen(target))) &&
|
||||
(tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD))) {
|
||||
write_csr(target, CSR_TDATA1, 0);
|
||||
}
|
||||
|
@ -2196,9 +2198,9 @@ static int riscv011_resume(struct target *target, int current, uint32_t address,
|
|||
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
|
||||
|
||||
if (!current) {
|
||||
if (xlen(target) > 32) {
|
||||
if (riscv_xlen(target) > 32) {
|
||||
LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
|
||||
xlen(target));
|
||||
riscv_xlen(target));
|
||||
}
|
||||
int result = register_write(target, REG_PC, address);
|
||||
if (result != ERROR_OK)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,8 @@
|
|||
#include "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
|
||||
|
@ -332,7 +334,7 @@ static int riscv_examine(struct target *target)
|
|||
return tt->examine(target);
|
||||
}
|
||||
|
||||
static int riscv_poll(struct target *target)
|
||||
static int oldriscv_poll(struct target *target)
|
||||
{
|
||||
struct target_type *tt = get_target_type(target);
|
||||
return tt->poll(target);
|
||||
|
@ -376,7 +378,13 @@ 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("riscv_get_gdb_reg_list: rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid);
|
||||
if (r->rtos_hartid != -1)
|
||||
riscv_set_current_hartid(target, r->rtos_hartid);
|
||||
else
|
||||
riscv_set_current_hartid(target, 0);
|
||||
|
||||
switch (reg_class) {
|
||||
case REG_CLASS_GENERAL:
|
||||
|
@ -395,6 +403,7 @@ static int riscv_get_gdb_reg_list(struct target *target,
|
|||
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];
|
||||
}
|
||||
|
||||
|
@ -474,7 +483,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
|
|||
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, set_field(current_mstatus,
|
||||
buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus,
|
||||
ie_mask, 0));
|
||||
|
||||
reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
|
||||
|
@ -492,11 +501,11 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
|
|||
if (now - start > timeout_ms) {
|
||||
LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
|
||||
riscv_halt(target);
|
||||
riscv_poll(target);
|
||||
oldriscv_poll(target);
|
||||
return ERROR_TARGET_TIMEOUT;
|
||||
}
|
||||
|
||||
int result = riscv_poll(target);
|
||||
int result = oldriscv_poll(target);
|
||||
if (result != ERROR_OK) {
|
||||
return result;
|
||||
}
|
||||
|
@ -514,12 +523,12 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
|
|||
|
||||
// Restore Interrupts
|
||||
LOG_DEBUG("Restoring Interrupts");
|
||||
buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus);
|
||||
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, saved_pc);
|
||||
buf_set_u64(buf, 0, info->xlen[0], saved_pc);
|
||||
if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) {
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
@ -527,7 +536,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
|
|||
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, saved_regs[r->number]);
|
||||
buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]);
|
||||
if (r->type->set(r, buf) != ERROR_OK) {
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
@ -572,7 +581,7 @@ struct target_type riscv_target =
|
|||
.examine = riscv_examine,
|
||||
|
||||
/* poll current target status */
|
||||
.poll = riscv_poll,
|
||||
.poll = oldriscv_poll,
|
||||
|
||||
.halt = riscv_halt,
|
||||
.resume = riscv_resume,
|
||||
|
@ -599,3 +608,400 @@ struct target_type riscv_target =
|
|||
|
||||
.run_algorithm = riscv_run_algorithm,
|
||||
};
|
||||
|
||||
/*** 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);
|
||||
LOG_DEBUG("polling hart %d", hartid);
|
||||
|
||||
/* If there's no new event then there's nothing to do. */
|
||||
riscv_set_current_hartid(target, hartid);
|
||||
assert((riscv_was_halted(target) && riscv_is_halted(target)) || !riscv_was_halted(target));
|
||||
if (riscv_was_halted(target) || !riscv_is_halted(target))
|
||||
return 0;
|
||||
|
||||
/* If we got here then this must be the first poll during which this
|
||||
* hart halted. We need to synchronize the hart's state with the
|
||||
* debugger, and inform the outer polling loop that there's something
|
||||
* to do. */
|
||||
r->hart_state[hartid] = RISCV_HART_HALTED;
|
||||
r->on_halt(target);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*** OpenOCD Interface ***/
|
||||
int riscv_openocd_poll(struct target *target)
|
||||
{
|
||||
LOG_DEBUG("polling all harts");
|
||||
if (riscv_rtos_enabled(target)) {
|
||||
/* Check every hart for an event. */
|
||||
int triggered_hart = -1;
|
||||
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 halted");
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
target->rtos->current_threadid = triggered_hart + 1;
|
||||
target->rtos->current_thread = triggered_hart + 1;
|
||||
|
||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
return ERROR_OK;
|
||||
} else {
|
||||
return riscv_poll_hart(target, riscv_current_hartid(target));
|
||||
}
|
||||
}
|
||||
|
||||
int riscv_openocd_halt(struct target *target)
|
||||
{
|
||||
int out = riscv_halt_all_harts(target);
|
||||
if (out != ERROR_OK)
|
||||
return out;
|
||||
|
||||
target->state = TARGET_HALTED;
|
||||
return out;
|
||||
}
|
||||
|
||||
int riscv_openocd_resume(
|
||||
struct target *target,
|
||||
int current,
|
||||
uint32_t address,
|
||||
int handle_breakpoints,
|
||||
int debug_execution
|
||||
) {
|
||||
if (!current) {
|
||||
LOG_ERROR("resume-at-pc unimplemented");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int out = riscv_resume_all_harts(target);
|
||||
if (out != ERROR_OK)
|
||||
return out;
|
||||
|
||||
target->state = TARGET_RUNNING;
|
||||
return out;
|
||||
}
|
||||
|
||||
int riscv_openocd_step(
|
||||
struct target *target,
|
||||
int current,
|
||||
uint32_t address,
|
||||
int handle_breakpoints
|
||||
) {
|
||||
if (!current) {
|
||||
LOG_ERROR("step-at-pc unimplemented");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int out = riscv_step_rtos_hart(target);
|
||||
if (out != ERROR_OK)
|
||||
return out;
|
||||
|
||||
target->state = TARGET_RUNNING;
|
||||
return out;
|
||||
}
|
||||
|
||||
/*** RISC-V Interface ***/
|
||||
|
||||
void riscv_info_init(riscv_info_t *r)
|
||||
{
|
||||
memset(r, 0, sizeof(*r));
|
||||
r->dtm_version = 1;
|
||||
|
||||
/* FIXME: The RTOS gets enabled before the target gets initialized. */
|
||||
r->rtos_enabled = true;
|
||||
|
||||
for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
|
||||
/* FIXME: I need to rip out Tim's probing sequence, as it
|
||||
* disrupts the running code. For now, I'm just hard-coding
|
||||
* XLEN to 64 for all cores at reset. */
|
||||
r->xlen[h] = 64;
|
||||
r->hart_state[h] = RISCV_HART_UNKNOWN;
|
||||
|
||||
for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
|
||||
r->valid_saved_registers[h][e] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void riscv_save_register(struct target *target, int regno)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
int hartno = r->current_hartid;
|
||||
LOG_DEBUG("riscv_save_register(%d, %d)", hartno, regno);
|
||||
assert(r->valid_saved_registers[hartno][regno] == false);
|
||||
r->valid_saved_registers[hartno][regno] = true;
|
||||
r->saved_registers[hartno][regno] = riscv_get_register(target, hartno, regno);
|
||||
}
|
||||
|
||||
uint64_t riscv_peek_register(struct target *target, int regno)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
int hartno = r->current_hartid;
|
||||
LOG_DEBUG("riscv_peek_register(%d, %d)", hartno, regno);
|
||||
assert(r->valid_saved_registers[hartno][regno] == true);
|
||||
return r->saved_registers[hartno][regno];
|
||||
}
|
||||
|
||||
void riscv_overwrite_register(struct target *target, int regno, uint64_t newval)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
int hartno = r->current_hartid;
|
||||
LOG_DEBUG("riscv_overwrite_register(%d, %d)", hartno, regno);
|
||||
assert(r->valid_saved_registers[hartno][regno] == true);
|
||||
r->saved_registers[hartno][regno] = newval;
|
||||
}
|
||||
|
||||
void riscv_restore_register(struct target *target, int regno)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
int hartno = r->current_hartid;
|
||||
LOG_DEBUG("riscv_restore_register(%d, %d)", hartno, regno);
|
||||
assert(r->valid_saved_registers[hartno][regno] == true);
|
||||
r->valid_saved_registers[hartno][regno] = false;
|
||||
riscv_set_register(target, hartno, regno, r->saved_registers[hartno][regno]);
|
||||
}
|
||||
|
||||
int riscv_halt_all_harts(struct target *target)
|
||||
{
|
||||
if (riscv_rtos_enabled(target)) {
|
||||
for (int i = 0; i < riscv_count_harts(target); ++i)
|
||||
riscv_halt_one_hart(target, i);
|
||||
} else {
|
||||
riscv_halt_one_hart(target, riscv_current_hartid(target));
|
||||
}
|
||||
|
||||
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 (r->hart_state[hartid] == RISCV_HART_UNKNOWN) {
|
||||
r->hart_state[hartid] = riscv_is_halted(target) ? RISCV_HART_HALTED : RISCV_HART_RUNNING;
|
||||
if (riscv_was_halted(target)) {
|
||||
LOG_WARNING("Connected to hart %d, which was halted. s0, s1, and pc were overwritten by your previous debugger session and cannot be restored.", hartid);
|
||||
r->on_halt(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (riscv_was_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)
|
||||
{
|
||||
if (riscv_rtos_enabled(target)) {
|
||||
for (int i = 0; i < riscv_count_harts(target); ++i)
|
||||
riscv_resume_one_hart(target, i);
|
||||
} else {
|
||||
riscv_resume_one_hart(target, riscv_current_hartid(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 (r->hart_state[hartid] == RISCV_HART_UNKNOWN) {
|
||||
r->hart_state[hartid] = riscv_is_halted(target) ? RISCV_HART_HALTED : RISCV_HART_RUNNING;
|
||||
if (!riscv_was_halted(target)) {
|
||||
LOG_ERROR("Asked to resume hart %d, which was in an unknown state", hartid);
|
||||
r->on_resume(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (!riscv_was_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);
|
||||
r->hart_state[hartid] = RISCV_HART_RUNNING;
|
||||
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(r->hart_state[hartid] == RISCV_HART_HALTED);
|
||||
r->on_step(target);
|
||||
r->step_current_hart(target);
|
||||
/* FIXME: There's a race condition with step. */
|
||||
r->hart_state[hartid] = RISCV_HART_RUNNING;
|
||||
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];
|
||||
}
|
||||
|
||||
void riscv_enable_rtos(struct target *target)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
r->rtos_enabled = true;
|
||||
}
|
||||
|
||||
bool riscv_rtos_enabled(const struct target *target)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
return r->rtos_enabled;
|
||||
}
|
||||
|
||||
void riscv_set_current_hartid(struct target *target, int hartid)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
register_cache_invalidate(target->reg_cache);
|
||||
r->current_hartid = hartid;
|
||||
r->select_current_hart(target);
|
||||
|
||||
/* Update the register list's widths. */
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int riscv_current_hartid(const struct target *target)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
assert(riscv_rtos_enabled(target) || target->coreid == r->current_hartid);
|
||||
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)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
r->rtos_hartid = hartid;
|
||||
}
|
||||
|
||||
int riscv_count_harts(struct target *target)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
bool riscv_has_register(struct target *target, int hartid, int regid)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void riscv_set_register(struct target *target, int hartid, enum gdb_regno regid, uint64_t value)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
LOG_DEBUG("writing register %d on hart %d", regid, hartid);
|
||||
return r->set_register(target, hartid, regid, value);
|
||||
}
|
||||
|
||||
uint64_t riscv_get_register(struct target *target, int hartid, enum gdb_regno regid)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
LOG_DEBUG("reading register %d on hart %d", regid, hartid);
|
||||
return r->get_register(target, hartid, regid);
|
||||
}
|
||||
|
||||
bool riscv_is_halted(struct target *target)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
return r->is_halted(target);
|
||||
}
|
||||
|
||||
bool riscv_was_halted(struct target *target)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
assert(r->hart_state[r->current_hartid] != RISCV_HART_UNKNOWN);
|
||||
return r->hart_state[r->current_hartid] == RISCV_HART_HALTED;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
#define RISCV_H
|
||||
|
||||
#include "opcodes.h"
|
||||
#include "gdb_regs.h"
|
||||
|
||||
/* The register cache is staticly allocated. */
|
||||
#define RISCV_MAX_HARTS 32
|
||||
#define RISCV_MAX_REGISTERS 5000
|
||||
|
||||
extern struct target_type riscv011_target;
|
||||
extern struct target_type riscv013_target;
|
||||
|
@ -9,15 +14,75 @@ extern struct target_type riscv013_target;
|
|||
/*
|
||||
* Definitions shared by code supporting all RISC-V versions.
|
||||
*/
|
||||
typedef uint64_t riscv_reg_t;
|
||||
|
||||
enum riscv_hart_state {
|
||||
RISCV_HART_UNKNOWN,
|
||||
RISCV_HART_HALTED,
|
||||
RISCV_HART_RUNNING,
|
||||
};
|
||||
|
||||
enum riscv_halt_reason {
|
||||
RISCV_HALT_INTERRUPT,
|
||||
RISCV_HALT_BREAKPOINT,
|
||||
RISCV_HALT_SINGLESTEP,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
unsigned dtm_version;
|
||||
struct command_context *cmd_ctx;
|
||||
void *version_specific;
|
||||
/* Width of a GPR (and many other things) in bits. */
|
||||
uint8_t xlen;
|
||||
|
||||
/* When the RTOS is enabled this target is expected to handle all the
|
||||
* harts in the system. When it's disabled this just uses the regular
|
||||
* multi-target mode. */
|
||||
bool rtos_enabled;
|
||||
|
||||
/* The hart that the RTOS thinks is currently being debugged. */
|
||||
int rtos_hartid;
|
||||
|
||||
/* The hart that is currently being debugged. Note that this is
|
||||
* different than the hartid that the RTOS is expected to use. This
|
||||
* one will change all the time, it's more of a global argument to
|
||||
* every function than an actual */
|
||||
int current_hartid;
|
||||
|
||||
/* Enough space to store all the registers we might need to save. */
|
||||
/* FIXME: This should probably be a bunch of register caches. */
|
||||
uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
|
||||
bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
|
||||
|
||||
/* The register cache points into here. */
|
||||
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
|
||||
|
||||
/* It's possible that each core has a different supported ISA set. */
|
||||
int xlen[RISCV_MAX_HARTS];
|
||||
|
||||
/* The state of every hart. */
|
||||
enum riscv_hart_state hart_state[RISCV_MAX_HARTS];
|
||||
|
||||
/* Helper functions that target the various RISC-V debug spec
|
||||
* implementations. */
|
||||
riscv_reg_t (*get_register)(struct target *, int, int);
|
||||
void (*set_register)(struct target *, int, int, uint64_t);
|
||||
void (*select_current_hart)(struct target *);
|
||||
bool (*is_halted)(struct target *target);
|
||||
void (*halt_current_hart)(struct target *);
|
||||
void (*resume_current_hart)(struct target *target);
|
||||
void (*step_current_hart)(struct target *target);
|
||||
void (*on_halt)(struct target *target);
|
||||
void (*on_resume)(struct target *target);
|
||||
void (*on_step)(struct target *target);
|
||||
enum riscv_halt_reason (*halt_reason)(struct target *target);
|
||||
} riscv_info_t;
|
||||
|
||||
/* Everything needs the RISC-V specific info structure, so here's a nice macro
|
||||
* that provides that. */
|
||||
static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused));
|
||||
static inline riscv_info_t *riscv_info(const struct target *target)
|
||||
{ return target->arch_info; }
|
||||
#define RISCV_INFO(R) riscv_info_t *R = riscv_info(target);
|
||||
|
||||
extern uint8_t ir_dtmcontrol[1];
|
||||
extern struct scan_field select_dtmcontrol;
|
||||
extern uint8_t ir_dbus[1];
|
||||
|
@ -25,43 +90,88 @@ extern struct scan_field select_dbus;
|
|||
extern uint8_t ir_idcode[1];
|
||||
extern struct scan_field select_idcode;
|
||||
|
||||
/*** Version-independent functions that we don't want in the main address space. ***/
|
||||
/*** OpenOCD Interface */
|
||||
int riscv_openocd_poll(struct target *target);
|
||||
|
||||
static uint32_t load(const struct target *target, unsigned int rd,
|
||||
unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||
static uint32_t load(const struct target *target, unsigned int rd,
|
||||
unsigned int base, uint16_t offset)
|
||||
{
|
||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||
switch (info->xlen) {
|
||||
case 32:
|
||||
return lw(rd, base, offset);
|
||||
case 64:
|
||||
return ld(rd, base, offset);
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
int riscv_openocd_halt(struct target *target);
|
||||
|
||||
static uint32_t store(const struct target *target, unsigned int src,
|
||||
unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||
static uint32_t store(const struct target *target, unsigned int src,
|
||||
unsigned int base, uint16_t offset)
|
||||
{
|
||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||
switch (info->xlen) {
|
||||
case 32:
|
||||
return sw(src, base, offset);
|
||||
case 64:
|
||||
return sd(src, base, offset);
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
int riscv_openocd_resume(
|
||||
struct target *target,
|
||||
int current,
|
||||
uint32_t address,
|
||||
int handle_breakpoints,
|
||||
int debug_execution
|
||||
);
|
||||
|
||||
static unsigned xlen(const struct target *target) __attribute__ ((unused));
|
||||
static unsigned xlen(const struct target *target)
|
||||
{
|
||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||
return info->xlen;
|
||||
}
|
||||
int riscv_openocd_step(
|
||||
struct target *target,
|
||||
int current,
|
||||
uint32_t address,
|
||||
int handle_breakpoints
|
||||
);
|
||||
|
||||
/*** RISC-V Interface ***/
|
||||
|
||||
/* Initializes the shared RISC-V structure. */
|
||||
void riscv_info_init(riscv_info_t *r);
|
||||
|
||||
/* Functions that save and restore registers. */
|
||||
void riscv_save_register(struct target *target, int regno);
|
||||
uint64_t riscv_peek_register(struct target *target, int regno);
|
||||
void riscv_overwrite_register(struct target *target, int regno, uint64_t newval);
|
||||
void riscv_restore_register(struct target *target, int regno);
|
||||
|
||||
/* Run control, possibly for multiple harts. The _all_harts versions resume
|
||||
* all the enabled harts, which when running in RTOS mode is all the harts on
|
||||
* the system. */
|
||||
int riscv_halt_all_harts(struct target *target);
|
||||
int riscv_halt_one_hart(struct target *target, int hartid);
|
||||
int riscv_resume_all_harts(struct target *target);
|
||||
int riscv_resume_one_hart(struct target *target, int hartid);
|
||||
|
||||
/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
|
||||
* then the only hart. */
|
||||
int riscv_step_rtos_hart(struct target *target);
|
||||
|
||||
/* Returns XLEN for the given (or current) hart. */
|
||||
int riscv_xlen(const struct target *target);
|
||||
int riscv_xlen_of_hart(const struct target *target, int hartid);
|
||||
|
||||
/* Enables RISC-V RTOS support for this target. This causes the coreid field
|
||||
* of the generic target struct to be ignored, and instead for operations to
|
||||
* apply to all the harts in the system. */
|
||||
void riscv_enable_rtos(struct target *target);
|
||||
bool riscv_rtos_enabled(const struct target *target);
|
||||
|
||||
/* Sets the current hart, which is the hart that will actually be used when
|
||||
* issuing debug commands. */
|
||||
void riscv_set_current_hartid(struct target *target, int hartid);
|
||||
int riscv_current_hartid(const struct target *target);
|
||||
|
||||
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
|
||||
* without requiring multiple targets. */
|
||||
|
||||
/* When using the RTOS to debug, this selects the hart that is currently being
|
||||
* debugged. This doesn't propogate to the hardware. */
|
||||
void riscv_set_all_rtos_harts(struct target *target);
|
||||
void riscv_set_rtos_hartid(struct target *target, int hartid);
|
||||
|
||||
/* Lists the number of harts in the system, which are assumed to be
|
||||
* concecutive and start with mhartid=0. */
|
||||
int riscv_count_harts(struct target *target);
|
||||
|
||||
/* Returns TRUE if the target has the given register on the given hart. */
|
||||
bool riscv_has_register(struct target *target, int hartid, int regid);
|
||||
|
||||
/* Returns the value of the given register on the given hart. 32-bit registers
|
||||
* are zero extended to 64 bits. */
|
||||
void riscv_set_register(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
|
||||
riscv_reg_t riscv_get_register(struct target *target, int hid, enum gdb_regno rid);
|
||||
|
||||
/* Checks the state of the current hart -- "is_halted" checks the actual
|
||||
* on-device register, while "was_halted" checks the machine's state. */
|
||||
bool riscv_is_halted(struct target *target);
|
||||
bool riscv_was_halted(struct target *target);
|
||||
enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue