diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index bd48fe9b0..dbf2ce3db 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1248,6 +1248,31 @@ static int deassert_reset(struct target *target) return ERROR_OK; } +static void write_to_buf(uint8_t *buffer, uint64_t value, unsigned size) +{ + switch (size) { + case 8: + buffer[7] = value >> 56; + buffer[6] = value >> 48; + buffer[5] = value >> 40; + buffer[4] = value >> 32; + case 4: + buffer[3] = value >> 24; + buffer[2] = value >> 16; + case 2: + buffer[1] = value >> 8; + case 1: + buffer[0] = value; + break; + default: + assert(false); + } +} + +/** + * Read the requested memory, taking care to execute every read exactly once, + * even if cmderr=busy is encountered. + */ static int read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { @@ -1314,6 +1339,9 @@ static int read_memory(struct target *target, target_addr_t address, return ERROR_FAIL; } + // Program has been executed once. d_addr contains address+size, and d_data + // contains *address. + /* The rest of this program is designed to be fast so it reads various * DMI registers directly. */ int d_data = (r_data - riscv_debug_buffer_addr(target)) / 4; @@ -1325,15 +1353,17 @@ static int read_memory(struct target *target, target_addr_t address, * case we need to back off a bit and try again. There's two * termination conditions to this loop: a non-BUSY error message, or * the data was all copied. */ - riscv_addr_t cur_addr = address; + riscv_addr_t cur_addr = riscv_read_debug_buffer_x(target, d_addr); riscv_addr_t fin_addr = address + (count * size); - bool this_is_last_read = false; LOG_DEBUG("reading until final address 0x%" PRIx64, fin_addr); - while (!this_is_last_read) { - riscv_addr_t start = (cur_addr - address) / size; - LOG_DEBUG("reading burst at address 0x%" TARGET_PRIxADDR - "; start=0x%" - TARGET_PRIxADDR, cur_addr, start); + while (cur_addr < fin_addr) { + // Invariant: + // d_data contains *addr + // d_addr contains addr + size + + unsigned start = (cur_addr - address) / size; + LOG_DEBUG("creating burst to read address 0x%" TARGET_PRIxADDR + " up to 0x%" TARGET_PRIxADDR "; start=0x%d", cur_addr, fin_addr, start); assert(cur_addr >= address && cur_addr < fin_addr); struct riscv_batch *batch = riscv_batch_alloc( target, @@ -1341,23 +1371,12 @@ static int read_memory(struct target *target, target_addr_t address, info->dmi_busy_delay + info->ac_busy_delay); size_t reads = 0; - size_t rereads = reads; - for (riscv_addr_t i = start; i < count; ++i) { - if (i == count - 1) { - // don't do actual read in this batch, - // we will do it later after we disable autoexec - // - // this is done to avoid reading more memory than requested - // which in some special cases(like reading stack located - // at the very top of RAM) may cause an exception - this_is_last_read = true; - } else { - size_t const index = - riscv_batch_add_dmi_read( + for (riscv_addr_t addr = cur_addr; addr < fin_addr; addr += size) { + size_t const index = + riscv_batch_add_dmi_read( batch, riscv013_debug_buffer_register(target, r_data)); - assert(index == reads); - } + assert(index == reads); reads++; if (riscv_batch_full(batch)) @@ -1366,27 +1385,21 @@ static int read_memory(struct target *target, target_addr_t address, riscv_batch_run(batch); - // Note that if the scan resulted in a Busy DMI response, it - // is this read to abstractcs that will cause the dmi_busy_delay - // to be incremented if necessary. The loop condition above - // catches the case where no writes went through at all. - - bool retry_batch_transaction = false; + // Wait for the target to finish performing the last abstract command, + // and update our copy of cmderr. uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS); while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) abstractcs = dmi_read(target, DMI_ABSTRACTCS); info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); + switch (info->cmderr) { case CMDERR_NONE: LOG_DEBUG("successful (partial?) memory read"); break; case CMDERR_BUSY: - LOG_DEBUG("memory read resulted in busy response; " - "this_is_last_read=%d", this_is_last_read); + LOG_DEBUG("memory read resulted in busy response"); increase_ac_busy_delay(target); riscv013_clear_abstract_error(target); - retry_batch_transaction = true; - riscv_batch_free(batch); break; default: LOG_ERROR("error when reading memory, abstractcs=0x%08lx", (long)abstractcs); @@ -1397,82 +1410,45 @@ static int read_memory(struct target *target, target_addr_t address, riscv_batch_free(batch); return ERROR_FAIL; } - if (retry_batch_transaction) { - this_is_last_read = false; - switch (riscv_xlen(target)) { - case 64: - riscv013_write_debug_buffer(target, d_addr + 1, cur_addr >> 32); - case 32: - riscv013_write_debug_buffer(target, d_addr, cur_addr); - break; - default: - LOG_ERROR("unknown XLEN %d", riscv_xlen(target)); - return ERROR_FAIL; - } + // Figure out how far we managed to read. + riscv_addr_t next_addr = riscv_read_debug_buffer_x(target, d_addr); + LOG_DEBUG("Batch read [0x%" TARGET_PRIxADDR ", 0x%" TARGET_PRIxADDR + "); reads=%d", cur_addr, next_addr, (unsigned) reads); + assert(next_addr >= address && next_addr <= fin_addr); + assert(info->cmderr != CMDERR_NONE || + next_addr == cur_addr + reads * size); - if (riscv013_execute_debug_buffer(target) != ERROR_OK) { - uint32_t acs = dmi_read(target, DMI_ABSTRACTCS); - LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs); - riscv013_clear_abstract_error(target); - riscv_set_register(target, GDB_REGNO_S0, s0); - riscv_set_register(target, GDB_REGNO_S1, s1); - LOG_ERROR(" exiting with ERROR_FAIL"); - return ERROR_FAIL; - } + // Now read whatever we got out of the batch. + unsigned rereads = 0; + for (riscv_addr_t addr = cur_addr - size; addr < next_addr - size; + addr += size) { + riscv_addr_t offset = addr - address; - continue; - } + uint64_t dmi_out = riscv_batch_get_dmi_read(batch, rereads); + uint32_t value = get_field(dmi_out, DTM_DMI_DATA); + write_to_buf(buffer + offset, value, size); - for (size_t i = start; i < start + reads; ++i) { - riscv_addr_t offset = size*i; - riscv_addr_t t_addr = address + offset; - uint8_t *t_buffer = buffer + offset; - uint32_t value; - - if (this_is_last_read && i == start + reads - 1) { - riscv013_set_autoexec(target, d_data, 0); - - // Access debug buffer without executing a program. This - // address logic was taken from program.c. - int const off = (r_data - - riscv_debug_buffer_addr(program.target)) / - sizeof(program.debug_buffer[0]); - value = riscv_read_debug_buffer(target, off); - } else { - uint64_t dmi_out = riscv_batch_get_dmi_read(batch, rereads); - value = get_field(dmi_out, DTM_DMI_DATA); - } - rereads++; - switch (size) { - case 1: - t_buffer[0] = value; - break; - case 2: - t_buffer[0] = value; - t_buffer[1] = value >> 8; - break; - case 4: - t_buffer[0] = value; - t_buffer[1] = value >> 8; - t_buffer[2] = value >> 16; - t_buffer[3] = value >> 24; - break; - default: - LOG_ERROR("unsupported access size: %d", size); - return ERROR_FAIL; - } - - LOG_DEBUG("M[0x%08lx] reads 0x%08x", (long)t_addr, value); + LOG_DEBUG("M[0x%" TARGET_PRIxADDR "] reads 0x%08x", addr, value); } riscv_batch_free(batch); - cur_addr += reads * size; + cur_addr = next_addr; } riscv013_set_autoexec(target, d_data, 0); + + // Read the last word. + + // Access debug buffer without executing a program. This + // address logic was taken from program.c. + uint32_t value = riscv013_read_debug_buffer(target, d_data); + riscv_addr_t addr = cur_addr - size; + write_to_buf(buffer + addr - address, value, size); + LOG_DEBUG("M[0x%" TARGET_PRIxADDR "] reads 0x%08x", addr, value); + riscv_set_register(target, GDB_REGNO_S0, s0); riscv_set_register(target, GDB_REGNO_S1, s1); return ERROR_OK;