Adds support for RISCV Access Memory Abstract Commands (#394)
* Adds support for RISCV Access Memory Abstract Commands The Access Memory Abstract Command is one of the three optional methods for reading and writing memory on a complient RISCV debug module. The previous two options were already implemented in OpenOCD. Implementation Notes: - aamvirtual is hard-coded to false until the design for accessing virtual addresses is finalized. - aamsizes corresponding to 8b, 16b, 32b, and 64b are supported. 128b support is postponed until it is required, as it will mean changes to the read/write_abstract_arg() interface to pass 128b values. - aampostincrement is not used and hard-coded to false. * Changes from review. * Additional lint fixes and a typo from last commit. * Fixing a clang error. * Fixes a last-minute change that broke writes with width > 8b. * Removing memcpy after adding read_from_buf().debug-log-reg-failure
parent
efce094b40
commit
cd7eea6d76
|
@ -844,7 +844,7 @@ static riscv_reg_t read_abstract_arg(struct target *target, unsigned index,
|
||||||
unsigned offset = index * size_bits / 32;
|
unsigned offset = index * size_bits / 32;
|
||||||
switch (size_bits) {
|
switch (size_bits) {
|
||||||
default:
|
default:
|
||||||
LOG_ERROR("Unsupported size: %d", size_bits);
|
LOG_ERROR("Unsupported size: %d bits", size_bits);
|
||||||
return ~0;
|
return ~0;
|
||||||
case 64:
|
case 64:
|
||||||
dmi_read(target, &v, DMI_DATA0 + offset + 1);
|
dmi_read(target, &v, DMI_DATA0 + offset + 1);
|
||||||
|
@ -863,7 +863,7 @@ static int write_abstract_arg(struct target *target, unsigned index,
|
||||||
unsigned offset = index * size_bits / 32;
|
unsigned offset = index * size_bits / 32;
|
||||||
switch (size_bits) {
|
switch (size_bits) {
|
||||||
default:
|
default:
|
||||||
LOG_ERROR("Unsupported size: %d", size_bits);
|
LOG_ERROR("Unsupported size: %d bits", size_bits);
|
||||||
return ERROR_FAIL;
|
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);
|
||||||
|
@ -986,6 +986,45 @@ static int register_write_abstract(struct target *target, uint32_t number,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the AAMSIZE field of a memory access abstract command based on
|
||||||
|
* the width (bits).
|
||||||
|
*/
|
||||||
|
static uint32_t abstract_memory_size(unsigned width)
|
||||||
|
{
|
||||||
|
switch (width) {
|
||||||
|
case 8:
|
||||||
|
return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 0);
|
||||||
|
case 16:
|
||||||
|
return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 1);
|
||||||
|
case 32:
|
||||||
|
return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 2);
|
||||||
|
case 64:
|
||||||
|
return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 3);
|
||||||
|
case 128:
|
||||||
|
return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 4);
|
||||||
|
default:
|
||||||
|
LOG_ERROR("Unsupported memory width: %d", width);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a memory access abstract command.
|
||||||
|
*/
|
||||||
|
static uint32_t access_memory_command(struct target *target, bool virtual,
|
||||||
|
unsigned width, bool postincrement, bool write)
|
||||||
|
{
|
||||||
|
uint32_t command = set_field(0, AC_ACCESS_MEMORY_CMDTYPE, 2);
|
||||||
|
command = set_field(command, AC_ACCESS_MEMORY_AAMVIRTUAL, virtual);
|
||||||
|
command |= abstract_memory_size(width);
|
||||||
|
command = set_field(command, AC_ACCESS_MEMORY_AAMPOSTINCREMENT,
|
||||||
|
postincrement);
|
||||||
|
command = set_field(command, AC_ACCESS_MEMORY_WRITE, write);
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
static int examine_progbuf(struct target *target)
|
static int examine_progbuf(struct target *target)
|
||||||
{
|
{
|
||||||
riscv013_info_t *info = get_info(target);
|
riscv013_info_t *info = get_info(target);
|
||||||
|
@ -1912,6 +1951,30 @@ static int deassert_reset(struct target *target)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @par size in bytes
|
||||||
|
*/
|
||||||
|
static void read_from_buf(uint64_t *value, const uint8_t *buffer, unsigned size)
|
||||||
|
{
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
*value = buffer[0];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*value = buffer[0]
|
||||||
|
| ((uint64_t) buffer[1] << 8);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
*value = buffer[0]
|
||||||
|
| ((uint64_t) buffer[1] << 8)
|
||||||
|
| ((uint64_t) buffer[2] << 16)
|
||||||
|
| ((uint64_t) buffer[3] << 24);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @par size in bytes
|
* @par size in bytes
|
||||||
*/
|
*/
|
||||||
|
@ -2298,6 +2361,128 @@ static int batch_run(const struct target *target, struct riscv_batch *batch)
|
||||||
return riscv_batch_run(batch);
|
return riscv_batch_run(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Performs a memory read using memory access abstract commands. The read sizes
|
||||||
|
* supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16 byte
|
||||||
|
* aamsize fields in the memory access abstract command.
|
||||||
|
*/
|
||||||
|
static int read_memory_abstract(struct target *target, target_addr_t address,
|
||||||
|
uint32_t size, uint32_t count, uint8_t *buffer)
|
||||||
|
{
|
||||||
|
int result = ERROR_OK;
|
||||||
|
|
||||||
|
LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
|
||||||
|
size, address);
|
||||||
|
|
||||||
|
memset(buffer, 0, count*size);
|
||||||
|
|
||||||
|
/* Convert the size (bytes) to width (bits) */
|
||||||
|
unsigned width = size << 3;
|
||||||
|
if (width > 64) {
|
||||||
|
/* TODO: Add 128b support if it's ever used. Involves modifying
|
||||||
|
read/write_abstract_arg() to work on two 64b values. */
|
||||||
|
LOG_ERROR("Unsupported size: %d bits", size);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the command (physical address, postincrement, read) */
|
||||||
|
uint32_t command = access_memory_command(target, false, width, true, false);
|
||||||
|
|
||||||
|
/* Execute the reads */
|
||||||
|
uint8_t *p = buffer;
|
||||||
|
bool updateaddr = true;
|
||||||
|
unsigned width32 = (width + 31) / 32 * 32;
|
||||||
|
for (uint32_t c = 0; c < count; c++) {
|
||||||
|
/* Only update the addres initially and let postincrement update it */
|
||||||
|
if (updateaddr) {
|
||||||
|
/* Set arg1 to the address: address + c * size */
|
||||||
|
result = write_abstract_arg(target, 1, address, riscv_xlen(target));
|
||||||
|
if (result != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to write arg1 during read_memory_abstract().");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute the command */
|
||||||
|
result = execute_abstract_command(target, command);
|
||||||
|
if (result != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to execute command read_memory_abstract().");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy arg0 to buffer (rounded width up to nearest 32) */
|
||||||
|
riscv_reg_t value = read_abstract_arg(target, 0, width32);
|
||||||
|
write_to_buf(p, value, size);
|
||||||
|
|
||||||
|
updateaddr = false;
|
||||||
|
p += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Performs a memory write using memory access abstract commands. The write
|
||||||
|
* sizes supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16
|
||||||
|
* byte aamsize fields in the memory access abstract command.
|
||||||
|
*/
|
||||||
|
static int write_memory_abstract(struct target *target, target_addr_t address,
|
||||||
|
uint32_t size, uint32_t count, const uint8_t *buffer)
|
||||||
|
{
|
||||||
|
int result = ERROR_OK;
|
||||||
|
|
||||||
|
LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
|
||||||
|
size, address);
|
||||||
|
|
||||||
|
/* Convert the size (bytes) to width (bits) */
|
||||||
|
unsigned width = size << 3;
|
||||||
|
if (width > 64) {
|
||||||
|
/* TODO: Add 128b support if it's ever used. Involves modifying
|
||||||
|
read/write_abstract_arg() to work on two 64b values. */
|
||||||
|
LOG_ERROR("Unsupported size: %d bits", width);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the command (physical address, postincrement, write) */
|
||||||
|
uint32_t command = access_memory_command(target, false, width, true, true);
|
||||||
|
|
||||||
|
/* Execute the writes */
|
||||||
|
const uint8_t *p = buffer;
|
||||||
|
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);
|
||||||
|
result = write_abstract_arg(target, 0, value, riscv_xlen(target));
|
||||||
|
if (result != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to write arg0 during write_memory_abstract().");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only update the addres initially and let postincrement update it */
|
||||||
|
if (updateaddr) {
|
||||||
|
/* Set arg1 to the address: address + c * size */
|
||||||
|
result = write_abstract_arg(target, 1, address, riscv_xlen(target));
|
||||||
|
if (result != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to write arg1 during write_memory_abstract().");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute the command */
|
||||||
|
result = execute_abstract_command(target, command);
|
||||||
|
if (result != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to execute command write_memory_abstract().");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateaddr = false;
|
||||||
|
p += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the requested memory, taking care to execute every read exactly once,
|
* Read the requested memory, taking care to execute every read exactly once,
|
||||||
* even if cmderr=busy is encountered.
|
* even if cmderr=busy is encountered.
|
||||||
|
@ -2698,8 +2883,7 @@ static int read_memory(struct target *target, target_addr_t address,
|
||||||
if (info->progbufsize >= 2)
|
if (info->progbufsize >= 2)
|
||||||
return read_memory_progbuf(target, address, size, count, buffer);
|
return read_memory_progbuf(target, address, size, count, buffer);
|
||||||
|
|
||||||
LOG_ERROR("Don't know how to read memory on this target.");
|
return read_memory_abstract(target, address, size, count, buffer);
|
||||||
return ERROR_FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_memory_bus_v0(struct target *target, target_addr_t address,
|
static int write_memory_bus_v0(struct target *target, target_addr_t address,
|
||||||
|
@ -2717,6 +2901,7 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
|
||||||
|
|
||||||
/* B.8 Writing Memory, single write check if we write in one go */
|
/* B.8 Writing Memory, single write check if we write in one go */
|
||||||
if (count == 1) { /* count is in bytes here */
|
if (count == 1) { /* count is in bytes here */
|
||||||
|
/* TODO: Test with read_from_buf(&value, t_buffer, size) */
|
||||||
/* check the size */
|
/* check the size */
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -2761,6 +2946,7 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
|
||||||
t_addr = address + offset;
|
t_addr = address + offset;
|
||||||
t_buffer = buffer + offset;
|
t_buffer = buffer + offset;
|
||||||
|
|
||||||
|
/* TODO: Test with read_from_buf(&value, t_buffer, size) */
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
value = t_buffer[0];
|
value = t_buffer[0];
|
||||||
|
@ -2943,6 +3129,7 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
|
||||||
unsigned offset = size*i;
|
unsigned offset = size*i;
|
||||||
const uint8_t *t_buffer = buffer + offset;
|
const uint8_t *t_buffer = buffer + offset;
|
||||||
|
|
||||||
|
/* TODO: Test with read_from_buf(&value, t_buffer, size)*/
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -3085,8 +3272,7 @@ static int write_memory(struct target *target, target_addr_t address,
|
||||||
if (info->progbufsize >= 2)
|
if (info->progbufsize >= 2)
|
||||||
return write_memory_progbuf(target, address, size, count, buffer);
|
return write_memory_progbuf(target, address, size, count, buffer);
|
||||||
|
|
||||||
LOG_ERROR("Don't know how to write memory on this target.");
|
return write_memory_abstract(target, address, size, count, buffer);
|
||||||
return ERROR_FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int arch_state(struct target *target)
|
static int arch_state(struct target *target)
|
||||||
|
|
Loading…
Reference in New Issue