From cd7eea6d764a8f93abf5cf74d1cab99c3e839025 Mon Sep 17 00:00:00 2001 From: dave-estes-syzexion <53795406+dave-estes-syzexion@users.noreply.github.com> Date: Mon, 19 Aug 2019 17:03:20 -0400 Subject: [PATCH] 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(). --- src/target/riscv/riscv-013.c | 198 +++++++++++++++++++++++++++++++++-- 1 file changed, 192 insertions(+), 6 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index cb4f9fef6..28276661a 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -844,7 +844,7 @@ static riscv_reg_t read_abstract_arg(struct target *target, unsigned index, unsigned offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d", size_bits); + LOG_ERROR("Unsupported size: %d bits", size_bits); return ~0; case 64: 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; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d", size_bits); + LOG_ERROR("Unsupported size: %d bits", size_bits); return ERROR_FAIL; case 64: 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; } +/* + * 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) { riscv013_info_t *info = get_info(target); @@ -1912,6 +1951,30 @@ static int deassert_reset(struct target *target) 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 */ @@ -2298,6 +2361,128 @@ static int batch_run(const struct target *target, struct riscv_batch *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, * 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) return read_memory_progbuf(target, address, size, count, buffer); - LOG_ERROR("Don't know how to read memory on this target."); - return ERROR_FAIL; + return read_memory_abstract(target, address, size, count, buffer); } 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 */ if (count == 1) { /* count is in bytes here */ + /* TODO: Test with read_from_buf(&value, t_buffer, size) */ /* check the size */ switch (size) { case 1: @@ -2761,6 +2946,7 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, t_addr = address + offset; t_buffer = buffer + offset; + /* TODO: Test with read_from_buf(&value, t_buffer, size) */ switch (size) { case 1: value = t_buffer[0]; @@ -2943,6 +3129,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: @@ -3085,8 +3272,7 @@ static int write_memory(struct target *target, target_addr_t address, if (info->progbufsize >= 2) return write_memory_progbuf(target, address, size, count, buffer); - LOG_ERROR("Don't know how to write memory on this target."); - return ERROR_FAIL; + return write_memory_abstract(target, address, size, count, buffer); } static int arch_state(struct target *target)