Make OpenOCD work when there is no program buffer.
Fixed abstract register access for registers that aren't XLEN wide. Avoided excessive errors cases where we attempted to execute a fence but failed. Don't mark all the CSRs as caller-save. gdb was saving/restoring dscratch, which broke function calls as a side effect. dscratch is accessible for people who really know what they're doing, but gdb should never quietly access it. The same is probably true for other CSRs. Change-Id: I7bcdbbcb7e3c22ad92cbc205bf537c1fe548b160sba_tests
parent
ee93a9b2f1
commit
bb2c25c5ce
|
@ -611,14 +611,14 @@ static int execute_abstract_command(struct target *target, uint32_t command)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static riscv_reg_t read_abstract_arg(struct target *target, unsigned index)
|
static riscv_reg_t read_abstract_arg(struct target *target, unsigned index,
|
||||||
|
unsigned size_bits)
|
||||||
{
|
{
|
||||||
riscv_reg_t value = 0;
|
riscv_reg_t value = 0;
|
||||||
unsigned xlen = riscv_xlen(target);
|
unsigned offset = index * size_bits / 32;
|
||||||
unsigned offset = index * xlen / 32;
|
switch (size_bits) {
|
||||||
switch (xlen) {
|
|
||||||
default:
|
default:
|
||||||
LOG_ERROR("Unsupported xlen: %d", xlen);
|
LOG_ERROR("Unsupported size: %d", size_bits);
|
||||||
return ~0;
|
return ~0;
|
||||||
case 64:
|
case 64:
|
||||||
value |= ((uint64_t) dmi_read(target, DMI_DATA0 + offset + 1)) << 32;
|
value |= ((uint64_t) dmi_read(target, DMI_DATA0 + offset + 1)) << 32;
|
||||||
|
@ -629,14 +629,13 @@ static riscv_reg_t read_abstract_arg(struct target *target, unsigned index)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_abstract_arg(struct target *target, unsigned index,
|
static int write_abstract_arg(struct target *target, unsigned index,
|
||||||
riscv_reg_t value)
|
riscv_reg_t value, unsigned size_bits)
|
||||||
{
|
{
|
||||||
unsigned xlen = riscv_xlen(target);
|
unsigned offset = index * size_bits / 32;
|
||||||
unsigned offset = index * xlen / 32;
|
switch (size_bits) {
|
||||||
switch (xlen) {
|
|
||||||
default:
|
default:
|
||||||
LOG_ERROR("Unsupported xlen: %d", xlen);
|
LOG_ERROR("Unsupported size: %d", size_bits);
|
||||||
return ~0;
|
return ERROR_FAIL;
|
||||||
case 64:
|
case 64:
|
||||||
dmi_write(target, DMI_DATA0 + offset + 1, value >> 32);
|
dmi_write(target, DMI_DATA0 + offset + 1, value >> 32);
|
||||||
case 32:
|
case 32:
|
||||||
|
@ -687,7 +686,7 @@ static int register_read_abstract(struct target *target, uint64_t *value,
|
||||||
RISCV013_INFO(info);
|
RISCV013_INFO(info);
|
||||||
|
|
||||||
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
|
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
|
||||||
!info->abstract_read_fpr_supported)
|
!info->abstract_write_fpr_supported)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
|
if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
|
||||||
!info->abstract_read_csr_supported)
|
!info->abstract_read_csr_supported)
|
||||||
|
@ -711,7 +710,7 @@ static int register_read_abstract(struct target *target, uint64_t *value,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
*value = read_abstract_arg(target, 0);
|
*value = read_abstract_arg(target, 0, size);
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
@ -732,7 +731,7 @@ static int register_write_abstract(struct target *target, uint32_t number,
|
||||||
AC_ACCESS_REGISTER_TRANSFER |
|
AC_ACCESS_REGISTER_TRANSFER |
|
||||||
AC_ACCESS_REGISTER_WRITE);
|
AC_ACCESS_REGISTER_WRITE);
|
||||||
|
|
||||||
if (write_abstract_arg(target, 0, value) != ERROR_OK)
|
if (write_abstract_arg(target, 0, value, size) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
int result = execute_abstract_command(target, command);
|
int result = execute_abstract_command(target, command);
|
||||||
|
@ -946,16 +945,31 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return register size in bits. */
|
||||||
|
static unsigned register_size(struct target *target, unsigned number)
|
||||||
|
{
|
||||||
|
/* If reg_cache hasn't been initialized yet, make a guess. We need this for
|
||||||
|
* when this function is called during examine(). */
|
||||||
|
if (target->reg_cache)
|
||||||
|
return target->reg_cache->reg_list[number].size;
|
||||||
|
else
|
||||||
|
return riscv_xlen(target);
|
||||||
|
}
|
||||||
|
|
||||||
static int register_write_direct(struct target *target, unsigned number,
|
static int register_write_direct(struct target *target, unsigned number,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
{
|
{
|
||||||
|
RISCV013_INFO(info);
|
||||||
|
RISCV_INFO(r);
|
||||||
|
|
||||||
LOG_DEBUG("[%d] reg[0x%x] <- 0x%" PRIx64, riscv_current_hartid(target),
|
LOG_DEBUG("[%d] reg[0x%x] <- 0x%" PRIx64, riscv_current_hartid(target),
|
||||||
number, value);
|
number, value);
|
||||||
|
|
||||||
int result = register_write_abstract(target, number, value,
|
int result = register_write_abstract(target, number, value,
|
||||||
riscv_xlen(target));
|
register_size(target, number));
|
||||||
if (result == ERROR_OK)
|
if (result == ERROR_OK ||
|
||||||
return ERROR_OK;
|
info->progbufsize + r->impebreak < 2)
|
||||||
|
return result;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
riscv_program_init(&program, target);
|
riscv_program_init(&program, target);
|
||||||
|
@ -1011,10 +1025,14 @@ static int register_write_direct(struct target *target, unsigned number,
|
||||||
/** Actually read registers from the target right now. */
|
/** Actually read registers from the target right now. */
|
||||||
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
|
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
|
||||||
{
|
{
|
||||||
int result = register_read_abstract(target, value, number,
|
RISCV013_INFO(info);
|
||||||
riscv_xlen(target));
|
RISCV_INFO(r);
|
||||||
|
|
||||||
if (result != ERROR_OK) {
|
int result = register_read_abstract(target, value, number,
|
||||||
|
register_size(target, number));
|
||||||
|
|
||||||
|
if (result != ERROR_OK &&
|
||||||
|
info->progbufsize + r->impebreak >= 2) {
|
||||||
assert(number != GDB_REGNO_S0);
|
assert(number != GDB_REGNO_S0);
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -1224,9 +1242,18 @@ static int examine(struct target *target)
|
||||||
info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
|
info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
|
||||||
info->progbufsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE);
|
info->progbufsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE);
|
||||||
|
|
||||||
|
LOG_INFO("datacount=%d progbufsize=%d", info->datacount, info->progbufsize);
|
||||||
|
|
||||||
RISCV_INFO(r);
|
RISCV_INFO(r);
|
||||||
r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK);
|
r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK);
|
||||||
|
|
||||||
|
if (info->progbufsize + r->impebreak < 2) {
|
||||||
|
LOG_WARNING("We won't be able to execute fence instructions on this "
|
||||||
|
"target. Memory may not always appear consistent. "
|
||||||
|
"(progbufsize=%d, impebreak=%d)", info->progbufsize,
|
||||||
|
r->impebreak);
|
||||||
|
}
|
||||||
|
|
||||||
/* Before doing anything else we must first enumerate the harts. */
|
/* Before doing anything else we must first enumerate the harts. */
|
||||||
|
|
||||||
/* Don't call any riscv_* functions until after we've counted the number of
|
/* Don't call any riscv_* functions until after we've counted the number of
|
||||||
|
@ -2400,14 +2427,26 @@ int riscv013_dmi_write_u64_bits(struct target *target)
|
||||||
return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
|
return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int maybe_execute_fence_i(struct target *target)
|
||||||
|
{
|
||||||
|
RISCV013_INFO(info);
|
||||||
|
RISCV_INFO(r);
|
||||||
|
if (info->progbufsize + r->impebreak >= 2) {
|
||||||
|
struct riscv_program program;
|
||||||
|
riscv_program_init(&program, target);
|
||||||
|
if (riscv_program_fence_i(&program) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
if (riscv_program_exec(&program, target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* Helper Functions. */
|
/* Helper Functions. */
|
||||||
static int riscv013_on_step_or_resume(struct target *target, bool step)
|
static int riscv013_on_step_or_resume(struct target *target, bool step)
|
||||||
{
|
{
|
||||||
struct riscv_program program;
|
if (maybe_execute_fence_i(target) != ERROR_OK)
|
||||||
riscv_program_init(&program, target);
|
return ERROR_FAIL;
|
||||||
riscv_program_fence_i(&program);
|
|
||||||
if (riscv_program_exec(&program, target) != ERROR_OK)
|
|
||||||
LOG_ERROR("Unable to execute fence.i");
|
|
||||||
|
|
||||||
/* We want to twiddle some bits in the debug CSR so debugging works. */
|
/* We want to twiddle some bits in the debug CSR so debugging works. */
|
||||||
riscv_reg_t dcsr;
|
riscv_reg_t dcsr;
|
||||||
|
@ -2430,10 +2469,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct riscv_program program;
|
if (maybe_execute_fence_i(target) != ERROR_OK)
|
||||||
riscv_program_init(&program, target);
|
|
||||||
riscv_program_fence_i(&program);
|
|
||||||
if (riscv_program_exec(&program, target) != ERROR_OK)
|
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
/* Issue the resume command, and then wait for the current hart to resume. */
|
/* Issue the resume command, and then wait for the current hart to resume. */
|
||||||
|
|
|
@ -1884,7 +1884,6 @@ int riscv_init_registers(struct target *target)
|
||||||
* between). */
|
* between). */
|
||||||
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
|
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
|
||||||
struct reg *r = &target->reg_cache->reg_list[number];
|
struct reg *r = &target->reg_cache->reg_list[number];
|
||||||
r->caller_save = true;
|
|
||||||
r->dirty = false;
|
r->dirty = false;
|
||||||
r->valid = false;
|
r->valid = false;
|
||||||
r->exist = true;
|
r->exist = true;
|
||||||
|
@ -1896,6 +1895,7 @@ int riscv_init_registers(struct target *target)
|
||||||
* target is in theory allowed to change XLEN on us. But I expect a lot
|
* target is in theory allowed to change XLEN on us. But I expect a lot
|
||||||
* of other things to break in that case as well. */
|
* of other things to break in that case as well. */
|
||||||
if (number <= GDB_REGNO_XPR31) {
|
if (number <= GDB_REGNO_XPR31) {
|
||||||
|
r->caller_save = true;
|
||||||
switch (number) {
|
switch (number) {
|
||||||
case GDB_REGNO_ZERO:
|
case GDB_REGNO_ZERO:
|
||||||
r->name = "zero";
|
r->name = "zero";
|
||||||
|
@ -1997,10 +1997,12 @@ int riscv_init_registers(struct target *target)
|
||||||
r->group = "general";
|
r->group = "general";
|
||||||
r->feature = &feature_cpu;
|
r->feature = &feature_cpu;
|
||||||
} else if (number == GDB_REGNO_PC) {
|
} else if (number == GDB_REGNO_PC) {
|
||||||
|
r->caller_save = true;
|
||||||
sprintf(reg_name, "pc");
|
sprintf(reg_name, "pc");
|
||||||
r->group = "general";
|
r->group = "general";
|
||||||
r->feature = &feature_cpu;
|
r->feature = &feature_cpu;
|
||||||
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
|
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
|
||||||
|
r->caller_save = true;
|
||||||
if (riscv_supports_extension(target, 'D')) {
|
if (riscv_supports_extension(target, 'D')) {
|
||||||
r->reg_data_type = &type_ieee_double;
|
r->reg_data_type = &type_ieee_double;
|
||||||
r->size = 64;
|
r->size = 64;
|
||||||
|
|
Loading…
Reference in New Issue