Add support for 64-bit memory reads/writes (#419)

* 64-bit progbuf memory reads work.

Change-Id: Ia3dbc0ee39a31ed0e5c38bbb3d9e089b2533f399

* 64-bit writes work.

Change-Id: Iae78711d715b6682817bb7cce366b0094bda8b23

* Let targets indicate number of supported data bits.

This is used by the default memory read/write functions when creating an
aligned block.

I'm adding this mainly to ensure I get coverage of the 64-bit progbuf
memory read/write code.

Change-Id: Ie5909fe537c9ec3360a8d2837f84be00a63de77b

* Make mingw32 happy.

Change-Id: Iade8c1fdfc72ccafc82f2f34923577032b668916
This commit is contained in:
Tim Newsome 2019-11-04 11:04:30 -08:00 committed by GitHub
parent 20804cb4d2
commit f93ede5401
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 56 deletions

View File

@ -79,6 +79,11 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
return ERROR_OK;
}
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, sd(d, b, offset));
}
int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, sw(d, b, offset));
@ -94,6 +99,11 @@ int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
return riscv_program_insert(p, sb(d, b, offset));
}
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, ld(d, b, offset));
}
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, lw(d, b, offset));

View File

@ -55,10 +55,12 @@ int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_sa
/* Helpers to assemble various instructions. Return 0 on success. These might
* assemble into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);

View File

@ -1261,8 +1261,8 @@ static int register_write_direct(struct target *target, unsigned number,
RISCV013_INFO(info);
RISCV_INFO(r);
LOG_DEBUG("{%d} reg[0x%x] <- 0x%" PRIx64, riscv_current_hartid(target),
number, value);
LOG_DEBUG("{%d} %s <- 0x%" PRIx64, riscv_current_hartid(target),
gdb_regno_name(number), value);
int result = register_write_abstract(target, number, value,
register_size(target, number));
@ -1449,8 +1449,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
}
if (result == ERROR_OK) {
LOG_DEBUG("{%d} reg[0x%x] = 0x%" PRIx64, riscv_current_hartid(target),
number, *value);
LOG_DEBUG("{%d} %s = 0x%" PRIx64, riscv_current_hartid(target),
gdb_regno_name(number), *value);
}
return result;
@ -1949,25 +1949,32 @@ static int deassert_reset(struct target *target)
/**
* @par size in bytes
*/
static void read_from_buf(uint64_t *value, const uint8_t *buffer, unsigned size)
static uint64_t read_from_buf(const uint8_t *buffer, unsigned size)
{
switch (size) {
case 1:
*value = buffer[0];
break;
return buffer[0];
case 2:
*value = buffer[0]
return buffer[0]
| ((uint64_t) buffer[1] << 8);
break;
case 4:
*value = buffer[0]
return buffer[0]
| ((uint64_t) buffer[1] << 8)
| ((uint64_t) buffer[2] << 16)
| ((uint64_t) buffer[3] << 24);
break;
case 8:
return buffer[0]
| ((uint64_t) buffer[1] << 8)
| ((uint64_t) buffer[2] << 16)
| ((uint64_t) buffer[3] << 24)
| ((uint64_t) buffer[4] << 32)
| ((uint64_t) buffer[5] << 40)
| ((uint64_t) buffer[6] << 48)
| ((uint64_t) buffer[7] << 56);
default:
assert(false);
}
return -1;
}
/**
@ -2042,7 +2049,21 @@ static void log_memory_access(target_addr_t address, uint64_t value,
char fmt[80];
sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64,
address, read ? "read" : "write", size_bytes * 2);
value &= (((uint64_t) 0x1) << (size_bytes * 8)) - 1;
switch (size_bytes) {
case 1:
value &= 0xff;
break;
case 2:
value &= 0xffff;
break;
case 4:
value &= 0xffffffff;
break;
case 8:
break;
default:
assert(false);
}
LOG_DEBUG(fmt, value);
}
@ -2446,8 +2467,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
bool updateaddr = true;
for (uint32_t c = 0; c < count; c++) {
/* Move data to arg0 */
riscv_reg_t value = 0;
read_from_buf(&value, p, size);
riscv_reg_t value = read_from_buf(p, size);
result = write_abstract_arg(target, 0, value, riscv_xlen(target));
if (result != ERROR_OK) {
LOG_ERROR("Failed to write arg0 during write_memory_abstract().");
@ -2542,6 +2562,8 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
size_t reads = 0;
for (riscv_addr_t addr = read_addr; addr < fin_addr; addr += size) {
if (size > 4)
riscv_batch_add_dmi_read(batch, DMI_DATA1);
riscv_batch_add_dmi_read(batch, DMI_DATA0);
reads++;
@ -2577,7 +2599,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
dmi_write(target, DMI_ABSTRACTAUTO, 0);
uint32_t dmi_data0;
uint32_t dmi_data0, dmi_data1 = 0;
/* This is definitely a good version of the value that we
* attempted to read when we discovered that the target was
* busy. */
@ -2585,6 +2607,10 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
riscv_batch_free(batch);
goto error;
}
if (size > 4 && dmi_read(target, &dmi_data1, DMI_DATA1) != ERROR_OK) {
riscv_batch_free(batch);
goto error;
}
/* See how far we got, clobbering dmi_data0. */
result = register_read_direct(target, &next_read_addr,
@ -2593,8 +2619,9 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
riscv_batch_free(batch);
goto error;
}
write_to_buf(buffer + next_read_addr - 2 * size - address, dmi_data0, size);
log_memory_access(next_read_addr - 2 * size, dmi_data0, size, true);
uint64_t value64 = (((uint64_t) dmi_data1) << 32) | dmi_data0;
write_to_buf(buffer + next_read_addr - 2 * size - address, value64, size);
log_memory_access(next_read_addr - 2 * size, value64, size, true);
/* Restore the command, and execute it.
* Now DMI_DATA0 contains the next value just as it would if no
@ -2618,15 +2645,16 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
/* Now read whatever we got out of the batch. */
dmi_status_t status = DMI_STATUS_SUCCESS;
riscv_addr_t receive_addr = read_addr - size * 2;
unsigned read = 0;
for (size_t i = 0; i < reads; i++) {
riscv_addr_t receive_addr = read_addr + (i-2) * size;
assert(receive_addr < address + size * count);
if (receive_addr < address)
continue;
if (receive_addr > next_read_addr - (3 + ignore_last) * size)
break;
uint64_t dmi_out = riscv_batch_get_dmi_read(batch, i);
uint64_t dmi_out = riscv_batch_get_dmi_read(batch, read++);
status = get_field(dmi_out, DTM_DMI_OP);
if (status != DMI_STATUS_SUCCESS) {
/* If we're here because of busy count, dmi_busy_delay will
@ -2643,7 +2671,20 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
result = ERROR_FAIL;
goto error;
}
uint32_t value = get_field(dmi_out, DTM_DMI_DATA);
uint64_t value = get_field(dmi_out, DTM_DMI_DATA);
if (size > 4) {
dmi_out = riscv_batch_get_dmi_read(batch, read++);
status = get_field(dmi_out, DTM_DMI_OP);
if (status != DMI_STATUS_SUCCESS) {
LOG_WARNING("Batch memory read encountered DMI error %d. "
"Falling back on slower reads.", status);
riscv_batch_free(batch);
result = ERROR_FAIL;
goto error;
}
value <<= 32;
value |= get_field(dmi_out, DTM_DMI_DATA);
}
riscv_addr_t offset = receive_addr - address;
write_to_buf(buffer + offset, value, size);
log_memory_access(receive_addr, value, size, true);
@ -2660,11 +2701,14 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
if (count > 1) {
/* Read the penultimate word. */
uint32_t value;
if (dmi_read(target, &value, DMI_DATA0) != ERROR_OK)
uint32_t dmi_data0, dmi_data1 = 0;
if (dmi_read(target, &dmi_data0, DMI_DATA0) != ERROR_OK)
return ERROR_FAIL;
write_to_buf(buffer + size * (count-2), value, size);
log_memory_access(address + size * (count-2), value, size, true);
if (size > 4 && dmi_read(target, &dmi_data1, DMI_DATA1) != ERROR_OK)
return ERROR_FAIL;
uint64_t value64 = (((uint64_t) dmi_data1) << 32) | dmi_data0;
write_to_buf(buffer + size * (count-2), value64, size);
log_memory_access(address + size * (count-2), value64, size, true);
}
/* Read the last word. */
@ -2715,6 +2759,9 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
case 4:
riscv_program_lwr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0);
break;
case 8:
riscv_program_ldr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0);
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
@ -2740,6 +2787,7 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
write_to_buf(buffer, value, size);
log_memory_access(address, value, size, true);
if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK)
return ERROR_FAIL;
@ -2760,6 +2808,12 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
{
RISCV013_INFO(info);
if (riscv_xlen(target) < size * 8) {
LOG_ERROR("XLEN (%d) is too short for %d-bit memory read.",
riscv_xlen(target), size * 8);
return ERROR_FAIL;
}
int result = ERROR_OK;
LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
@ -2805,10 +2859,14 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
case 4:
riscv_program_lwr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
break;
case 8:
riscv_program_ldr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
}
if (riscv_enable_virtual && info->progbufsize >= 4 && get_field(mstatus, MSTATUS_MPRV))
riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
@ -3088,6 +3146,12 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
{
RISCV013_INFO(info);
if (riscv_xlen(target) < size * 8) {
LOG_ERROR("XLEN (%d) is too short for %d-bit memory write.",
riscv_xlen(target), size * 8);
return ERROR_FAIL;
}
LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address);
select_dmi(target);
@ -3124,8 +3188,11 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
case 4:
riscv_program_swr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
break;
case 8:
riscv_program_sdr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
break;
default:
LOG_ERROR("Unsupported size: %d", size);
LOG_ERROR("write_memory_progbuf(): Unsupported size: %d", size);
result = ERROR_FAIL;
goto error;
}
@ -3158,28 +3225,7 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
unsigned offset = size*i;
const uint8_t *t_buffer = buffer + offset;
/* TODO: Test with read_from_buf(&value, t_buffer, size)*/
uint32_t value;
switch (size) {
case 1:
value = t_buffer[0];
break;
case 2:
value = t_buffer[0]
| ((uint32_t) t_buffer[1] << 8);
break;
case 4:
value = t_buffer[0]
| ((uint32_t) t_buffer[1] << 8)
| ((uint32_t) t_buffer[2] << 16)
| ((uint32_t) t_buffer[3] << 24);
break;
default:
LOG_ERROR("unsupported access size: %d", size);
riscv_batch_free(batch);
result = ERROR_FAIL;
goto error;
}
uint64_t value = read_from_buf(t_buffer, size);
log_memory_access(address + offset, value, size, false);
cur_addr += size;
@ -3193,12 +3239,14 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
}
/* Write value. */
if (size > 4)
dmi_write(target, DMI_DATA1, value >> 32);
dmi_write(target, DMI_DATA0, value);
/* Write and execute command that moves value into S1 and
* executes program buffer. */
uint32_t command = access_register_command(target,
GDB_REGNO_S1, 32,
GDB_REGNO_S1, size > 4 ? 64 : 32,
AC_ACCESS_REGISTER_POSTEXEC |
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_WRITE);
@ -3214,6 +3262,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
setup_needed = false;
} else {
if (size > 4)
riscv_batch_add_dmi_write(batch, DMI_DATA1, value >> 32);
riscv_batch_add_dmi_write(batch, DMI_DATA0, value);
if (riscv_batch_full(batch))
break;

View File

@ -2457,7 +2457,7 @@ const struct command_registration riscv_command_handlers[] = {
COMMAND_REGISTRATION_DONE
};
unsigned riscv_address_bits(struct target *target)
static unsigned riscv_xlen_nonconst(struct target *target)
{
return riscv_xlen(target);
}
@ -2500,7 +2500,8 @@ struct target_type riscv_target = {
.commands = riscv_command_handlers,
.address_bits = riscv_address_bits
.address_bits = riscv_xlen_nonconst,
.data_bits = riscv_xlen_nonconst
};
/*** RISC-V Interface ***/
@ -2609,7 +2610,7 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter)
return r->misa[hartid] & (1 << num);
}
int riscv_xlen(const struct target *target)
unsigned riscv_xlen(const struct target *target)
{
return riscv_xlen_of_hart(target, riscv_current_hartid(target));
}

View File

@ -218,7 +218,7 @@ int riscv_step_rtos_hart(struct target *target);
bool riscv_supports_extension(struct target *target, int hartid, char letter);
/* Returns XLEN for the given (or current) hart. */
int riscv_xlen(const struct target *target);
unsigned riscv_xlen(const struct target *target);
int riscv_xlen_of_hart(const struct target *target, int hartid);
bool riscv_rtos_enabled(const struct target *target);

View File

@ -1284,6 +1284,13 @@ unsigned target_address_bits(struct target *target)
return 32;
}
unsigned target_data_bits(struct target *target)
{
if (target->type->data_bits)
return target->type->data_bits(target);
return 32;
}
int target_profiling(struct target *target, uint32_t *samples,
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
{
@ -2189,9 +2196,11 @@ static int target_write_buffer_default(struct target *target,
{
uint32_t size;
/* Align up to maximum 4 bytes. The loop condition makes sure the next pass
/* Align up to maximum bytes. The loop condition makes sure the next pass
* will have something to do with the size we leave to it. */
for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) {
for (size = 1;
size < target_data_bits(target) / 8 && count >= size * 2 + (address & size);
size *= 2) {
if (address & size) {
int retval = target_write_memory(target, address, size, 1, buffer);
if (retval != ERROR_OK)
@ -2250,9 +2259,11 @@ static int target_read_buffer_default(struct target *target, target_addr_t addre
{
uint32_t size;
/* Align up to maximum 4 bytes. The loop condition makes sure the next pass
/* Align up to maximum bytes. The loop condition makes sure the next pass
* will have something to do with the size we leave to it. */
for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) {
for (size = 1;
size < target_data_bits(target) / 8 && count >= size * 2 + (address & size);
size *= 2) {
if (address & size) {
int retval = target_read_memory(target, address, size, 1, buffer);
if (retval != ERROR_OK)

View File

@ -664,6 +664,13 @@ target_addr_t target_address_max(struct target *target);
*/
unsigned target_address_bits(struct target *target);
/**
* Return the number of data bits this target supports.
*
* This routine is a wrapper for target->type->data_bits.
*/
unsigned target_data_bits(struct target *target);
/** Return the *name* of this targets current state */
const char *target_state_name(struct target *target);

View File

@ -296,6 +296,11 @@ struct target_type {
* typically be 32 for 32-bit targets, and 64 for 64-bit targets. If not
* implemented, it's assumed to be 32. */
unsigned (*address_bits)(struct target *target);
/* Return the number of data bits this target supports. This will
* typically be 32 for 32-bit targets, and 64 for 64-bit targets. If not
* implemented, it's assumed to be 32. */
unsigned (*data_bits)(struct target *target);
};
#endif /* OPENOCD_TARGET_TARGET_TYPE_H */