diff options
Diffstat (limited to 'src/target/riscv')
-rw-r--r-- | src/target/riscv/riscv-011.c | 21 | ||||
-rw-r--r-- | src/target/riscv/riscv-013.c | 275 | ||||
-rw-r--r-- | src/target/riscv/riscv.c | 51 | ||||
-rw-r--r-- | src/target/riscv/riscv.h | 4 | ||||
-rw-r--r-- | src/target/riscv/riscv_reg.c | 4 |
5 files changed, 199 insertions, 156 deletions
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index ef80e43..54de6a1 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -1458,8 +1458,8 @@ static int strict_step(struct target *target, bool announce) return ERROR_OK; } -static int step(struct target *target, int current, target_addr_t address, - int handle_breakpoints) +static int step(struct target *target, bool current, target_addr_t address, + bool handle_breakpoints) { jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); @@ -1889,8 +1889,16 @@ static int handle_halt(struct target *target, bool announce) if (target->debug_reason == DBG_REASON_BREAKPOINT) { int retval; - if (riscv_semihosting(target, &retval) != 0) - return retval; + /* Hotfix: Don't try to handle semihosting before the target is marked as examined. */ + /* TODO: The code should be rearranged so that: + * - Semihosting is not attempted before the target is examined. + * - When the target is already halted on a semihosting magic sequence + * at the time when OpenOCD connects to it, this semihosting attempt + * gets handled right after the examination. + */ + if (target_was_examined(target)) + if (riscv_semihosting(target, &retval) != SEMIHOSTING_NONE) + return retval; } if (announce) @@ -1952,8 +1960,9 @@ static int riscv011_poll(struct target *target) return poll_target(target, true); } -static int riscv011_resume(struct target *target, int current, - target_addr_t address, int handle_breakpoints, int debug_execution) +static int riscv011_resume(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, + bool debug_execution) { RISCV_INFO(r); jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index d02059c..46c61ca 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -145,6 +145,66 @@ typedef struct { struct target *target; } target_list_t; +struct ac_cache { + uint32_t *commands; + size_t size; +}; + +static int ac_cache_elem_comparator(const void *p_lhs, const void *p_rhs) +{ + uint32_t lhs = *(const uint32_t *)p_lhs; + uint32_t rhs = *(const uint32_t *)p_rhs; + if (lhs < rhs) + return -1; + if (lhs > rhs) + return 1; + return 0; +} + +static struct ac_cache ac_cache_construct(void) +{ + struct ac_cache cache = { + cache.commands = NULL, + cache.size = 0, + }; + return cache; +} + +static void ac_cache_free(struct ac_cache *cache) +{ + free(cache->commands); + cache->commands = NULL; + cache->size = 0; +} + +static void ac_cache_insert(struct ac_cache *cache, uint32_t command) +{ + assert(cache); + + size_t old_size = cache->size; + size_t new_size = old_size + 1; + size_t entry_size = sizeof(*cache->commands); + + uint32_t *commands = realloc(cache->commands, new_size * entry_size); + if (!commands) { + LOG_ERROR("Reallocation to %zu bytes failed", new_size * entry_size); + return; + } + + commands[old_size] = command; + cache->commands = commands; + cache->size = new_size; + + qsort(cache->commands, cache->size, entry_size, + ac_cache_elem_comparator); +} + +static bool ac_cache_contains(const struct ac_cache *cache, uint32_t command) +{ + return bsearch(&command, cache->commands, cache->size, + sizeof(*cache->commands), ac_cache_elem_comparator); +} + typedef struct { /* The indexed used to address this hart in its DM. */ unsigned int index; @@ -175,12 +235,7 @@ typedef struct { */ struct riscv_scan_delays learned_delays; - bool abstract_read_csr_supported; - bool abstract_write_csr_supported; - bool abstract_read_fpr_supported; - bool abstract_write_fpr_supported; - - yes_no_maybe_t has_aampostincrement; + struct ac_cache ac_not_supported_cache; /* Some fields from hartinfo. */ uint8_t datasize; @@ -651,6 +706,30 @@ clear_cmderr: return res; } +enum riscv_debug_reg_ordinal get_cmdtype(uint32_t command) +{ + switch (get_field(command, DM_COMMAND_CMDTYPE)) { + case 0: + return AC_ACCESS_REGISTER_ORDINAL; + case 1: + return AC_QUICK_ACCESS_ORDINAL; + case 2: + return AC_ACCESS_MEMORY_ORDINAL; + default: + assert(false && "Unknown command type value"); + return 0; + } +} + +static void mark_command_as_unsupported(struct target *target, uint32_t command) +{ + LOG_TARGET_DEBUG(target, "Caching the abstract " + "command 0x%" PRIx32 " as not supported", command); + log_debug_reg(target, get_cmdtype(command), + command, __FILE__, __LINE__, __func__); + ac_cache_insert(&get_info(target)->ac_not_supported_cache, command); +} + int riscv013_execute_abstract_command(struct target *target, uint32_t command, uint32_t *cmderr) { @@ -684,6 +763,9 @@ int riscv013_execute_abstract_command(struct target *target, uint32_t command, res = abstract_cmd_batch_check_and_clear_cmderr(target, batch, abstractcs_read_key, cmderr); + if (res != ERROR_OK && *cmderr == CMDERR_NOT_SUPPORTED) + mark_command_as_unsupported(target, command); + cleanup: riscv_batch_free(batch); return res; @@ -829,38 +911,35 @@ uint32_t riscv013_access_register_command(struct target *target, uint32_t number return command; } +static bool is_command_unsupported(struct target *target, uint32_t command) +{ + bool unsupported = ac_cache_contains(&get_info(target)->ac_not_supported_cache, command); + if (!unsupported) + return false; + + LOG_TARGET_DEBUG(target, "Abstract command 0x%" + PRIx32 " is cached as not supported", command); + log_debug_reg(target, get_cmdtype(command), + command, __FILE__, __LINE__, __func__); + return true; +} + static int register_read_abstract_with_size(struct target *target, riscv_reg_t *value, enum gdb_regno number, unsigned int size) { - RISCV013_INFO(info); - - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - !info->abstract_read_fpr_supported) - return ERROR_FAIL; - if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && - !info->abstract_read_csr_supported) - return ERROR_FAIL; /* The spec doesn't define abstract register numbers for vector registers. */ if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) return ERROR_FAIL; uint32_t command = riscv013_access_register_command(target, number, size, AC_ACCESS_REGISTER_TRANSFER); + if (is_command_unsupported(target, command)) + return ERROR_FAIL; uint32_t cmderr; int result = riscv013_execute_abstract_command(target, command, &cmderr); - if (result != ERROR_OK) { - if (cmderr == CMDERR_NOT_SUPPORTED) { - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - info->abstract_read_fpr_supported = false; - LOG_TARGET_INFO(target, "Disabling abstract command reads from FPRs."); - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - info->abstract_read_csr_supported = false; - LOG_TARGET_INFO(target, "Disabling abstract command reads from CSRs."); - } - } + if (result != ERROR_OK) return result; - } if (value) return read_abstract_arg(target, value, 0, size); @@ -879,23 +958,17 @@ static int register_read_abstract(struct target *target, riscv_reg_t *value, static int register_write_abstract(struct target *target, enum gdb_regno number, riscv_reg_t value) { - RISCV013_INFO(info); - dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - !info->abstract_write_fpr_supported) - return ERROR_FAIL; - if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && - !info->abstract_write_csr_supported) - return ERROR_FAIL; - const unsigned int size_bits = register_size(target, number); const uint32_t command = riscv013_access_register_command(target, number, size_bits, AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_WRITE); + if (is_command_unsupported(target, command)) + return ERROR_FAIL; + LOG_DEBUG_REG(target, AC_ACCESS_REGISTER, command); assert(size_bits % 32 == 0); const unsigned int size_in_words = size_bits / 32; @@ -915,18 +988,9 @@ static int register_write_abstract(struct target *target, enum gdb_regno number, uint32_t cmderr; res = abstract_cmd_batch_check_and_clear_cmderr(target, batch, abstractcs_read_key, &cmderr); + if (res != ERROR_OK && cmderr == CMDERR_NOT_SUPPORTED) + mark_command_as_unsupported(target, command); - if (res != ERROR_OK) { - if (cmderr == CMDERR_NOT_SUPPORTED) { - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - info->abstract_write_fpr_supported = false; - LOG_TARGET_INFO(target, "Disabling abstract command writes to FPRs."); - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - info->abstract_write_csr_supported = false; - LOG_TARGET_INFO(target, "Disabling abstract command writes to CSRs."); - } - } - } cleanup: riscv_batch_free(batch); return res; @@ -1709,6 +1773,10 @@ static void deinit_target(struct target *target) if (!info) return; + riscv013_info_t *vsinfo = info->version_specific; + if (vsinfo) + ac_cache_free(&vsinfo->ac_not_supported_cache); + riscv013_dm_free(target); free(info->version_specific); @@ -2824,17 +2892,7 @@ static int init_target(struct command_context *cmd_ctx, info->progbufsize = -1; reset_learned_delays(target); - /* Assume all these abstract commands are supported until we learn - * otherwise. - * TODO: The spec allows eg. one CSR to be able to be accessed abstractly - * while another one isn't. We don't track that this closely here, but in - * the future we probably should. */ - info->abstract_read_csr_supported = true; - info->abstract_write_csr_supported = true; - info->abstract_read_fpr_supported = true; - info->abstract_write_fpr_supported = true; - - info->has_aampostincrement = YNM_MAYBE; + info->ac_not_supported_cache = ac_cache_construct(); return ERROR_OK; } @@ -3724,16 +3782,22 @@ read_memory_abstract(struct target *target, const riscv_mem_access_args_t args) { assert(riscv_mem_access_is_read(args)); - RISCV013_INFO(info); - bool use_aampostincrement = info->has_aampostincrement != YNM_NO; - memset(args.read_buffer, 0, args.count * args.size); /* Convert the size (bytes) to width (bits) */ unsigned int width = args.size << 3; - /* Create the command (physical address, postincrement, read) */ - uint32_t command = access_memory_command(target, false, width, use_aampostincrement, false); + uint32_t command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ true, /* is_write = */ false); + bool use_aampostincrement = !is_command_unsupported(target, command); + if (!use_aampostincrement) + /* It is already known that this abstract memory + * access with aampostincrement=1 is not supported. + * So try aampostincrement=0 right away. + * + * TODO: check if new command is supported */ + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ false); /* Execute the reads */ uint8_t *p = args.read_buffer; @@ -3754,33 +3818,14 @@ read_memory_abstract(struct target *target, const riscv_mem_access_args_t args) /* Execute the command */ uint32_t cmderr; result = riscv013_execute_abstract_command(target, command, &cmderr); - - /* TODO: we need to modify error handling here. */ - /* NOTE: in case of timeout cmderr is set to CMDERR_NONE */ - if (info->has_aampostincrement == YNM_MAYBE) { - if (result == ERROR_OK) { - /* Safety: double-check that the address was really auto-incremented */ - riscv_reg_t new_address; - result = read_abstract_arg(target, &new_address, 1, riscv_xlen(target)); - if (result != ERROR_OK) - return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); - - if (new_address == args.address + args.size) { - LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target."); - info->has_aampostincrement = YNM_YES; - } else { - LOG_TARGET_WARNING(target, "Buggy aampostincrement! Address not incremented correctly."); - info->has_aampostincrement = YNM_NO; - } - } else { - /* Try the same access but with postincrement disabled. */ - command = access_memory_command(target, false, width, false, false); - result = riscv013_execute_abstract_command(target, command, &cmderr); - if (result == ERROR_OK) { - LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target."); - info->has_aampostincrement = YNM_NO; - } - } + if (use_aampostincrement && result != ERROR_OK && + cmderr == CMDERR_NOT_SUPPORTED) { + LOG_TARGET_DEBUG(target, "Trying the same abstract memory " + "read command, but without aampostincrement"); + use_aampostincrement = false; + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ false); + result = riscv013_execute_abstract_command(target, command, &cmderr); } /* TODO: @@ -3796,7 +3841,7 @@ read_memory_abstract(struct target *target, const riscv_mem_access_args_t args) return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); buf_set_u64(p, 0, 8 * args.size, value); - if (info->has_aampostincrement == YNM_YES) + if (use_aampostincrement) updateaddr = false; p += args.size; } @@ -3814,15 +3859,22 @@ write_memory_abstract(struct target *target, const riscv_mem_access_args_t args) { assert(riscv_mem_access_is_write(args)); - RISCV013_INFO(info); int result = ERROR_OK; - bool use_aampostincrement = info->has_aampostincrement != YNM_NO; /* Convert the size (bytes) to width (bits) */ unsigned int width = args.size << 3; - /* Create the command (physical address, postincrement, write) */ - uint32_t command = access_memory_command(target, false, width, use_aampostincrement, true); + uint32_t command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ true, /* is_write = */ true); + bool use_aampostincrement = !is_command_unsupported(target, command); + if (!use_aampostincrement) + /* It is already known that this abstract memory + * access with aampostincrement=1 is not supported. + * So try aampostincrement=0 right away. + * + * TODO: check if new command is supported */ + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ true); /* Execute the writes */ const uint8_t *p = args.write_buffer; @@ -3849,33 +3901,14 @@ write_memory_abstract(struct target *target, const riscv_mem_access_args_t args) /* Execute the command */ uint32_t cmderr; result = riscv013_execute_abstract_command(target, command, &cmderr); - - /* TODO: we need to modify error handling here. */ - /* NOTE: in case of timeout cmderr is set to CMDERR_NONE */ - if (info->has_aampostincrement == YNM_MAYBE) { - if (result == ERROR_OK) { - /* Safety: double-check that the address was really auto-incremented */ - riscv_reg_t new_address; - result = read_abstract_arg(target, &new_address, 1, riscv_xlen(target)); - if (result != ERROR_OK) - return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); - - if (new_address == args.address + args.size) { - LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target."); - info->has_aampostincrement = YNM_YES; - } else { - LOG_TARGET_WARNING(target, "Buggy aampostincrement! Address not incremented correctly."); - info->has_aampostincrement = YNM_NO; - } - } else { - /* Try the same access but with postincrement disabled. */ - command = access_memory_command(target, false, width, false, true); - result = riscv013_execute_abstract_command(target, command, &cmderr); - if (result == ERROR_OK) { - LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target."); - info->has_aampostincrement = YNM_NO; - } - } + if (use_aampostincrement && result != ERROR_OK && + cmderr == CMDERR_NOT_SUPPORTED) { + LOG_TARGET_DEBUG(target, "Trying the same abstract memory " + "write command, but without aampostincrement"); + use_aampostincrement = false; + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ true); + result = riscv013_execute_abstract_command(target, command, &cmderr); } /* TODO: @@ -3884,7 +3917,7 @@ write_memory_abstract(struct target *target, const riscv_mem_access_args_t args) if (result != ERROR_OK) return mem_access_result(MEM_ACCESS_SKIPPED_ABSTRACT_ACCESS_CMDERR); - if (info->has_aampostincrement == YNM_YES) + if (use_aampostincrement) updateaddr = false; p += args.size; } diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index cd31881..a55fc14 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -2433,8 +2433,8 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w return ERROR_FAIL; } -static int oldriscv_step(struct target *target, int current, uint32_t address, - int handle_breakpoints) +static int oldriscv_step(struct target *target, bool current, uint32_t address, + bool handle_breakpoints) { struct target_type *tt = get_target_type(target); if (!tt) @@ -2442,14 +2442,15 @@ static int oldriscv_step(struct target *target, int current, uint32_t address, return tt->step(target, current, address, handle_breakpoints); } -static int riscv_openocd_step_impl(struct target *target, int current, - target_addr_t address, int handle_breakpoints, int handle_callbacks); +static int riscv_openocd_step_impl(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, int handle_callbacks); -static int old_or_new_riscv_step_impl(struct target *target, int current, - target_addr_t address, int handle_breakpoints, int handle_callbacks) +static int old_or_new_riscv_step_impl(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, int handle_callbacks) { RISCV_INFO(r); - LOG_TARGET_DEBUG(target, "handle_breakpoints=%d", handle_breakpoints); + LOG_TARGET_DEBUG(target, "handle_breakpoints=%s", + handle_breakpoints ? "true" : "false"); if (!r->get_hart_state) return oldriscv_step(target, current, address, handle_breakpoints); else @@ -2457,8 +2458,8 @@ static int old_or_new_riscv_step_impl(struct target *target, int current, handle_callbacks); } -static int old_or_new_riscv_step(struct target *target, int current, - target_addr_t address, int handle_breakpoints) +static int old_or_new_riscv_step(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints) { return old_or_new_riscv_step_impl(target, current, address, handle_breakpoints, true /* handle callbacks*/); @@ -2822,8 +2823,8 @@ static int enable_watchpoints(struct target *target, bool *wp_is_set) /** * Get everything ready to resume. */ -static int resume_prep(struct target *target, int current, - target_addr_t address, int handle_breakpoints, int debug_execution) +static int resume_prep(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, bool debug_execution) { assert(target->state == TARGET_HALTED); RISCV_INFO(r); @@ -2864,8 +2865,8 @@ static int resume_prep(struct target *target, int current, * Resume all the harts that have been prepped, as close to instantaneous as * possible. */ -static int resume_go(struct target *target, int current, - target_addr_t address, int handle_breakpoints, int debug_execution) +static int resume_go(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, bool debug_execution) { assert(target->state == TARGET_HALTED); RISCV_INFO(r); @@ -2883,7 +2884,7 @@ static int resume_go(struct target *target, int current, return result; } -static int resume_finish(struct target *target, int debug_execution) +static int resume_finish(struct target *target, bool debug_execution) { assert(target->state == TARGET_HALTED); if (riscv_reg_cache_any_dirty(target, LOG_LVL_ERROR)) { @@ -2908,10 +2909,10 @@ static int resume_finish(struct target *target, int debug_execution) */ static int riscv_resume( struct target *target, - int current, + bool current, target_addr_t address, - int handle_breakpoints, - int debug_execution, + bool handle_breakpoints, + bool debug_execution, bool single_hart) { int result = ERROR_OK; @@ -2972,8 +2973,8 @@ static int riscv_resume( return result; } -static int riscv_target_resume(struct target *target, int current, - target_addr_t address, int handle_breakpoints, int debug_execution) +static int riscv_target_resume(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, bool debug_execution) { if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "Not halted."); @@ -3654,8 +3655,8 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, return ERROR_FAIL; /* Run algorithm */ - LOG_TARGET_DEBUG(target, "Resume at 0x%" TARGET_PRIxADDR, entry_point); - if (riscv_resume(target, 0, entry_point, 0, 1, true) != ERROR_OK) + LOG_TARGET_DEBUG(target, "resume at 0x%" TARGET_PRIxADDR, entry_point); + if (riscv_resume(target, false, entry_point, false, true, true) != ERROR_OK) return ERROR_FAIL; int64_t start = timeval_ms(); @@ -4196,8 +4197,8 @@ int riscv_openocd_poll(struct target *target) return ERROR_OK; } -static int riscv_openocd_step_impl(struct target *target, int current, - target_addr_t address, int handle_breakpoints, int handle_callbacks) +static int riscv_openocd_step_impl(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, int handle_callbacks) { LOG_TARGET_DEBUG(target, "stepping hart"); @@ -4291,8 +4292,8 @@ _exit: return success ? ERROR_OK : ERROR_FAIL; } -int riscv_openocd_step(struct target *target, int current, - target_addr_t address, int handle_breakpoints) +int riscv_openocd_step(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints) { return riscv_openocd_step_impl(target, current, address, handle_breakpoints, true /* handle_callbacks */); diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 031abe5..49d80f0 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -450,9 +450,9 @@ int riscv_halt(struct target *target); int riscv_openocd_step( struct target *target, - int current, + bool current, target_addr_t address, - int handle_breakpoints + bool handle_breakpoints ); /*** RISC-V Interface ***/ diff --git a/src/target/riscv/riscv_reg.c b/src/target/riscv/riscv_reg.c index 1a0d402..e35cd7f 100644 --- a/src/target/riscv/riscv_reg.c +++ b/src/target/riscv/riscv_reg.c @@ -56,7 +56,6 @@ static const char * const default_reg_names[GDB_REGNO_COUNT] = { [GDB_REGNO_T5] = "t5", [GDB_REGNO_T6] = "t6", [GDB_REGNO_PC] = "pc", - [GDB_REGNO_CSR0] = "csr0", [GDB_REGNO_PRIV] = "priv", [GDB_REGNO_FT0] = "ft0", [GDB_REGNO_FT1] = "ft1", @@ -196,7 +195,8 @@ const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno } if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) { init_custom_csr_names(target); - info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0); + if (!info->reg_names[regno]) + info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0); return info->reg_names[regno]; } assert(!"Encountered uninitialized entry in reg_names table"); |