X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Farc.c;h=cec6441a563ec332194726d135242a9ab9265092;hp=396bf8a4742dcd4eccfe0eaea249426b770fe29e;hb=HEAD;hpb=0af37282c742dfc3492348f44f66215939148fb0 diff --git a/src/target/arc.c b/src/target/arc.c index 396bf8a474..72e4d918de 100644 --- a/src/target/arc.c +++ b/src/target/arc.c @@ -1,11 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /*************************************************************************** * Copyright (C) 2013-2015,2019-2020 Synopsys, Inc. * * Frank Dols * * Mischa Jonker * * Anton Kolesov * * Evgeniy Didin * - * * - * SPDX-License-Identifier: GPL-2.0-or-later * ***************************************************************************/ @@ -33,7 +33,7 @@ * unexisting register is safe RAZ, rather then an error. * Note, core registers cannot be BCR. * - * In arc/cpu/ tcl files all regiters are defined as core, non-BCR aux + * In arc/cpu/ tcl files all registers are defined as core, non-BCR aux * and BCR aux, in "add-reg" command they are passed to three lists * respectively: core_reg_descriptions, aux_reg_descriptions, * bcr_reg_descriptions. @@ -48,6 +48,15 @@ */ +static int arc_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint); +static int arc_enable_watchpoints(struct target *target); +static int arc_enable_breakpoints(struct target *target); +static int arc_unset_breakpoint(struct target *target, + struct breakpoint *breakpoint); +static int arc_set_breakpoint(struct target *target, + struct breakpoint *breakpoint); +static int arc_single_step_core(struct target *target); void arc_reg_data_type_add(struct target *target, struct arc_reg_data_type *data_type) @@ -86,6 +95,26 @@ struct reg *arc_reg_get_by_name(struct reg_cache *first, return NULL; } +/** + * Reset internal states of caches. Must be called when entering debugging. + * + * @param target Target for which to reset caches states. + */ +static int arc_reset_caches_states(struct target *target) +{ + struct arc_common *arc = target_to_arc(target); + + LOG_DEBUG("Resetting internal variables of caches states"); + + /* Reset caches states. */ + arc->dcache_flushed = false; + arc->l2cache_flushed = false; + arc->icache_invalidated = false; + arc->dcache_invalidated = false; + arc->l2cache_invalidated = false; + + return ERROR_OK; +} /* Initialize arc_common structure, which passes to openocd target instance */ static int arc_init_arch_info(struct target *target, struct arc_common *arc, @@ -102,6 +131,15 @@ static int arc_init_arch_info(struct target *target, struct arc_common *arc, return ERROR_FAIL; } + /* On most ARC targets there is a dcache, so we enable its flushing + * by default. If there no dcache, there will be no error, just a slight + * performance penalty from unnecessary JTAG operations. */ + arc->has_dcache = true; + arc->has_icache = true; + /* L2$ is not available in a target by default. */ + arc->has_l2cache = false; + arc_reset_caches_states(target); + /* Add standard GDB data types */ INIT_LIST_HEAD(&arc->reg_data_types); struct arc_reg_data_type *std_types = calloc(ARRAY_SIZE(standard_gdb_types), @@ -168,7 +206,7 @@ int arc_reg_add(struct target *target, struct arc_reg_desc *arc_reg, arc->num_regs += 1; LOG_DEBUG( - "added register {name=%s, num=0x%x, type=%s%s%s%s}", + "added register {name=%s, num=0x%" PRIx32 ", type=%s%s%s%s}", arc_reg->name, arc_reg->arch_num, arc_reg->data_type->id, arc_reg->is_core ? ", core" : "", arc_reg->is_bcr ? ", bcr" : "", arc_reg->is_general ? ", general" : "" @@ -196,7 +234,7 @@ static int arc_get_register(struct reg *reg) if (desc->is_core) { /* Accessing to R61/R62 registers causes Jtag hang */ - if (desc->arch_num == CORE_R61_NUM || desc->arch_num == CORE_R62_NUM) { + if (desc->arch_num == ARC_R61 || desc->arch_num == ARC_R62) { LOG_ERROR("It is forbidden to read core registers 61 and 62."); return ERROR_FAIL; } @@ -218,7 +256,7 @@ static int arc_get_register(struct reg *reg) reg->dirty = false; LOG_DEBUG("Get register gdb_num=%" PRIu32 ", name=%s, value=0x%" PRIx32, - reg->number , desc->name, value); + reg->number, desc->name, value); return ERROR_OK; @@ -236,8 +274,8 @@ static int arc_set_register(struct reg *reg, uint8_t *buf) return ERROR_TARGET_NOT_HALTED; /* Accessing to R61/R62 registers causes Jtag hang */ - if (desc->is_core && (desc->arch_num == CORE_R61_NUM || - desc->arch_num == CORE_R62_NUM)) { + if (desc->is_core && (desc->arch_num == ARC_R61 || + desc->arch_num == ARC_R62)) { LOG_ERROR("It is forbidden to write core registers 61 and 62."); return ERROR_FAIL; } @@ -252,12 +290,12 @@ static int arc_set_register(struct reg *reg, uint8_t *buf) return ERROR_OK; } -const struct reg_arch_type arc_reg_type = { +static const struct reg_arch_type arc_reg_type = { .get = arc_get_register, .set = arc_set_register, }; -/* GDB register groups. For now we suport only general and "empty" */ +/* GDB register groups. For now we support only general and "empty" */ static const char * const reg_group_general = "general"; static const char * const reg_group_other = ""; @@ -274,7 +312,7 @@ static int arc_init_reg(struct target *target, struct reg *reg, /* Initialize struct reg */ reg->name = reg_desc->name; reg->size = 32; /* All register in ARC are 32-bit */ - reg->value = ®_desc->reg_value; + reg->value = reg_desc->reg_value; reg->type = &arc_reg_type; reg->arch_info = reg_desc; reg->caller_save = true; /* @todo should be configurable. */ @@ -519,7 +557,7 @@ int arc_reg_get_field(struct target *target, const char *reg_name, struct reg *reg = arc_reg_get_by_name(target->reg_cache, reg_name, true); if (!reg) { - LOG_ERROR("Requested register `%s' doens't exist.", reg_name); + LOG_ERROR("Requested register `%s' doesn't exist.", reg_name); return ERROR_ARC_REGISTER_NOT_FOUND; } @@ -546,7 +584,7 @@ int arc_reg_get_field(struct target *target, const char *reg_name, if (!reg->valid) CHECK_RETVAL(reg->type->get(reg)); - /* First do endiannes-safe read of register value + /* First do endianness-safe read of register value * then convert it to binary buffer for further * field extraction */ @@ -574,6 +612,27 @@ static int arc_get_register_value(struct target *target, const char *reg_name, return ERROR_OK; } +static int arc_set_register_value(struct target *target, const char *reg_name, + uint32_t value) +{ + LOG_DEBUG("reg_name=%s value=0x%08" PRIx32, reg_name, value); + + if (!(target && reg_name)) { + LOG_ERROR("Arguments cannot be NULL."); + return ERROR_FAIL; + } + + struct reg *reg = arc_reg_get_by_name(target->reg_cache, reg_name, true); + + if (!reg) + return ERROR_ARC_REGISTER_NOT_FOUND; + + uint8_t value_buf[4]; + buf_set_u32(value_buf, 0, 32, value); + CHECK_RETVAL(reg->type->set(reg, value_buf)); + + return ERROR_OK; +} /* Configure DCCM's */ static int arc_configure_dccm(struct target *target) @@ -696,6 +755,29 @@ static int arc_examine(struct target *target) return ERROR_OK; } +static int arc_exit_debug(struct target *target) +{ + uint32_t value; + struct arc_common *arc = target_to_arc(target); + + /* Do read-modify-write sequence, or DEBUG.UB will be reset unintentionally. */ + CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_DEBUG_REG, &value)); + value |= SET_CORE_FORCE_HALT; /* set the HALT bit */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DEBUG_REG, value)); + alive_sleep(1); + + target->state = TARGET_HALTED; + CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_HALTED)); + + if (debug_level >= LOG_LVL_DEBUG) { + LOG_DEBUG("core stopped (halted) debug-reg: 0x%08" PRIx32, value); + CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_STATUS32_REG, &value)); + LOG_DEBUG("core STATUS32: 0x%08" PRIx32, value); + } + + return ERROR_OK; +} + static int arc_halt(struct target *target) { uint32_t value, irq_state; @@ -794,27 +876,23 @@ static int arc_save_context(struct target *target) memset(aux_addrs, 0xff, aux_regs_size); for (i = 0; i < MIN(arc->num_core_regs, regs_to_scan); i++) { - struct reg *reg = &(reg_list[i]); + struct reg *reg = reg_list + i; struct arc_reg_desc *arc_reg = reg->arch_info; - if (!reg->valid && reg->exist) { - core_addrs[core_cnt] = arc_reg->arch_num; - core_cnt += 1; - } + if (!reg->valid && reg->exist) + core_addrs[core_cnt++] = arc_reg->arch_num; } for (i = arc->num_core_regs; i < regs_to_scan; i++) { - struct reg *reg = &(reg_list[i]); + struct reg *reg = reg_list + i; struct arc_reg_desc *arc_reg = reg->arch_info; - if (!reg->valid && reg->exist) { - aux_addrs[aux_cnt] = arc_reg->arch_num; - aux_cnt += 1; - } + if (!reg->valid && reg->exist) + aux_addrs[aux_cnt++] = arc_reg->arch_num; } /* Read data from target. */ if (core_cnt > 0) { retval = arc_jtag_read_core_reg(&arc->jtag_info, core_addrs, core_cnt, core_values); - if (ERROR_OK != retval) { + if (retval != ERROR_OK) { LOG_ERROR("Attempt to read core registers failed."); retval = ERROR_FAIL; goto exit; @@ -822,7 +900,7 @@ static int arc_save_context(struct target *target) } if (aux_cnt > 0) { retval = arc_jtag_read_aux_reg(&arc->jtag_info, aux_addrs, aux_cnt, aux_values); - if (ERROR_OK != retval) { + if (retval != ERROR_OK) { LOG_ERROR("Attempt to read aux registers failed."); retval = ERROR_FAIL; goto exit; @@ -832,30 +910,30 @@ static int arc_save_context(struct target *target) /* Parse core regs */ core_cnt = 0; for (i = 0; i < MIN(arc->num_core_regs, regs_to_scan); i++) { - struct reg *reg = &(reg_list[i]); + struct reg *reg = reg_list + i; struct arc_reg_desc *arc_reg = reg->arch_info; if (!reg->valid && reg->exist) { target_buffer_set_u32(target, reg->value, core_values[core_cnt]); - core_cnt += 1; reg->valid = true; reg->dirty = false; - LOG_DEBUG("Get core register regnum=%" PRIu32 ", name=%s, value=0x%08" PRIx32, + LOG_DEBUG("Get core register regnum=%u, name=%s, value=0x%08" PRIx32, i, arc_reg->name, core_values[core_cnt]); + core_cnt++; } } /* Parse aux regs */ aux_cnt = 0; for (i = arc->num_core_regs; i < regs_to_scan; i++) { - struct reg *reg = &(reg_list[i]); + struct reg *reg = reg_list + i; struct arc_reg_desc *arc_reg = reg->arch_info; if (!reg->valid && reg->exist) { target_buffer_set_u32(target, reg->value, aux_values[aux_cnt]); - aux_cnt += 1; reg->valid = true; reg->dirty = false; - LOG_DEBUG("Get aux register regnum=%" PRIu32 ", name=%s, value=0x%08" PRIx32, - i , arc_reg->name, aux_values[aux_cnt]); + LOG_DEBUG("Get aux register regnum=%u, name=%s, value=0x%08" PRIx32, + i, arc_reg->name, aux_values[aux_cnt]); + aux_cnt++; } } @@ -868,6 +946,45 @@ exit: return retval; } +/** + * Finds an actionpoint that triggered last actionpoint event, as specified by + * DEBUG.ASR. + * + * @param target + * @param actionpoint Pointer to be set to last active actionpoint. Pointer + * will be set to NULL if DEBUG.AH is 0. + */ +static int get_current_actionpoint(struct target *target, + struct arc_actionpoint **actionpoint) +{ + assert(target); + assert(actionpoint); + + uint32_t debug_ah; + /* Check if actionpoint caused halt */ + CHECK_RETVAL(arc_reg_get_field(target, "debug", "ah", + &debug_ah)); + + if (debug_ah) { + struct arc_common *arc = target_to_arc(target); + unsigned int ap; + uint32_t debug_asr; + CHECK_RETVAL(arc_reg_get_field(target, "debug", + "asr", &debug_asr)); + + for (ap = 0; debug_asr > 1; debug_asr >>= 1) + ap += 1; + + assert(ap < arc->actionpoints_num); + + *actionpoint = &(arc->actionpoints_list[ap]); + } else { + *actionpoint = NULL; + } + + return ERROR_OK; +} + static int arc_examine_debug_reason(struct target *target) { uint32_t debug_bh; @@ -887,8 +1004,20 @@ static int arc_examine_debug_reason(struct target *target) /* DEBUG.BH is set if core halted due to BRK instruction. */ target->debug_reason = DBG_REASON_BREAKPOINT; } else { - /* TODO: Add Actionpoint check when AP support will be introduced*/ - LOG_WARNING("Unknown debug reason"); + struct arc_actionpoint *actionpoint = NULL; + CHECK_RETVAL(get_current_actionpoint(target, &actionpoint)); + + if (actionpoint) { + if (!actionpoint->used) + LOG_WARNING("Target halted by an unused actionpoint."); + + if (actionpoint->type == ARC_AP_BREAKPOINT) + target->debug_reason = DBG_REASON_BREAKPOINT; + else if (actionpoint->type == ARC_AP_WATCHPOINT) + target->debug_reason = DBG_REASON_WATCHPOINT; + else + LOG_WARNING("Unknown type of actionpoint."); + } } return ERROR_OK; @@ -900,6 +1029,7 @@ static int arc_debug_entry(struct target *target) /* TODO: reset internal indicators of caches states, otherwise D$/I$ * will not be flushed/invalidated when required. */ + CHECK_RETVAL(arc_reset_caches_states(target)); CHECK_RETVAL(arc_examine_debug_reason(target)); return ERROR_OK; @@ -1093,7 +1223,7 @@ static int arc_restore_context(struct target *target) * Check before write, if aux and core count is greater than 0. */ if (core_cnt > 0) { retval = arc_jtag_write_core_reg(&arc->jtag_info, core_addrs, core_cnt, core_values); - if (ERROR_OK != retval) { + if (retval != ERROR_OK) { LOG_ERROR("Attempt to write to core registers failed."); retval = ERROR_FAIL; goto exit; @@ -1102,7 +1232,7 @@ static int arc_restore_context(struct target *target) if (aux_cnt > 0) { retval = arc_jtag_write_aux_reg(&arc->jtag_info, aux_addrs, aux_cnt, aux_values); - if (ERROR_OK != retval) { + if (retval != ERROR_OK) { LOG_ERROR("Attempt to write to aux registers failed."); retval = ERROR_FAIL; goto exit; @@ -1149,19 +1279,31 @@ static int arc_resume(struct target *target, int current, target_addr_t address, uint32_t value; struct reg *pc = &arc->core_and_aux_cache->reg_list[arc->pc_index_in_cache]; - LOG_DEBUG("current:%i, address:0x%08" TARGET_PRIxADDR ", handle_breakpoints(not supported yet):%i," + LOG_DEBUG("current:%i, address:0x%08" TARGET_PRIxADDR ", handle_breakpoints:%i," " debug_execution:%i", current, address, handle_breakpoints, debug_execution); + /* We need to reset ARC cache variables so caches + * would be invalidated and actual data + * would be fetched from memory. */ + CHECK_RETVAL(arc_reset_caches_states(target)); + if (target->state != TARGET_HALTED) { - LOG_WARNING("target not halted"); + LOG_TARGET_ERROR(target, "not halted"); return ERROR_TARGET_NOT_HALTED; } + if (!debug_execution) { + /* (gdb) continue = execute until we hit break/watch-point */ + target_free_all_working_areas(target); + CHECK_RETVAL(arc_enable_breakpoints(target)); + CHECK_RETVAL(arc_enable_watchpoints(target)); + } + /* current = 1: continue on current PC, otherwise continue at
*/ if (!current) { target_buffer_set_u32(target, pc->value, address); - pc->dirty = 1; - pc->valid = 1; + pc->dirty = true; + pc->valid = true; LOG_DEBUG("Changing the value of current PC to 0x%08" TARGET_PRIxADDR, address); } @@ -1176,12 +1318,25 @@ static int arc_resume(struct target *target, int current, target_addr_t address, resume_pc, pc->dirty, pc->valid); /* check if GDB tells to set our PC where to continue from */ - if ((pc->valid == 1) && (resume_pc == target_buffer_get_u32(target, pc->value))) { + if (pc->valid && resume_pc == target_buffer_get_u32(target, pc->value)) { value = target_buffer_get_u32(target, pc->value); LOG_DEBUG("resume Core (when start-core) with PC @:0x%08" PRIx32, value); CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_PC_REG, value)); } + /* the front-end may request us not to handle breakpoints here */ + if (handle_breakpoints) { + /* Single step past breakpoint at current address */ + struct breakpoint *breakpoint = breakpoint_find(target, resume_pc); + if (breakpoint) { + LOG_DEBUG("skipping past breakpoint at 0x%08" TARGET_PRIxADDR, + breakpoint->address); + CHECK_RETVAL(arc_unset_breakpoint(target, breakpoint)); + CHECK_RETVAL(arc_single_step_core(target)); + CHECK_RETVAL(arc_set_breakpoint(target, breakpoint)); + } + } + /* Restore IRQ state if not in debug_execution*/ if (!debug_execution) CHECK_RETVAL(arc_enable_interrupts(target, arc->irq_state)); @@ -1266,6 +1421,7 @@ static void arc_deinit_target(struct target *target) list_for_each_entry_safe(desc, k, &arc->bcr_reg_descriptions, list) free_reg_desc(desc); + free(arc->actionpoints_list); free(arc); } @@ -1285,10 +1441,598 @@ static int arc_target_create(struct target *target, Jim_Interp *interp) return ERROR_OK; } +/** + * Write 4-byte instruction to memory. This is like target_write_u32, however + * in case of little endian ARC instructions are in middle endian format, not + * little endian, so different type of conversion should be done. + * Middle endian: instruction "aabbccdd", stored as "bbaaddcc" + */ +static int arc_write_instruction_u32(struct target *target, uint32_t address, + uint32_t instr) +{ + uint8_t value_buf[4]; + if (!target_was_examined(target)) { + LOG_ERROR("Target not examined yet"); + return ERROR_FAIL; + } + + LOG_DEBUG("Address: 0x%08" PRIx32 ", value: 0x%08" PRIx32, address, + instr); + + if (target->endianness == TARGET_LITTLE_ENDIAN) + arc_h_u32_to_me(value_buf, instr); + else + h_u32_to_be(value_buf, instr); + + CHECK_RETVAL(target_write_buffer(target, address, 4, value_buf)); + + return ERROR_OK; +} + +/** + * Read 32-bit instruction from memory. It is like target_read_u32, however in + * case of little endian ARC instructions are in middle endian format, so + * different type of conversion should be done. + */ +static int arc_read_instruction_u32(struct target *target, uint32_t address, + uint32_t *value) +{ + uint8_t value_buf[4]; + + if (!target_was_examined(target)) { + LOG_ERROR("Target not examined yet"); + return ERROR_FAIL; + } + + *value = 0; + CHECK_RETVAL(target_read_buffer(target, address, 4, value_buf)); + + if (target->endianness == TARGET_LITTLE_ENDIAN) + *value = arc_me_to_h_u32(value_buf); + else + *value = be_to_h_u32(value_buf); + + LOG_DEBUG("Address: 0x%08" PRIx32 ", value: 0x%08" PRIx32, address, + *value); + + return ERROR_OK; +} + +/* Actionpoint mechanism allows to setup HW breakpoints + * and watchpoints. Each actionpoint is controlled by + * 3 aux registers: Actionpoint(AP) match mask(AP_AMM), AP match value(AP_AMV) + * and AP control(AC). + * This function is for setting/unsetting actionpoints: + * at - actionpoint target: trigger on mem/reg access + * tt - transaction type : trigger on r/w. */ +static int arc_configure_actionpoint(struct target *target, uint32_t ap_num, + uint32_t match_value, uint32_t control_tt, uint32_t control_at) +{ + struct arc_common *arc = target_to_arc(target); + + if (control_tt != AP_AC_TT_DISABLE) { + + if (arc->actionpoints_num_avail < 1) { + LOG_ERROR("No free actionpoints, maximum amount is %u", + arc->actionpoints_num); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* Names of register to set - 24 chars should be enough. Looks a little + * bit out-of-place for C code, but makes it aligned to the bigger + * concept of "ARC registers are defined in TCL" as far as possible. + */ + char ap_amv_reg_name[24], ap_amm_reg_name[24], ap_ac_reg_name[24]; + snprintf(ap_amv_reg_name, 24, "ap_amv%" PRIu32, ap_num); + snprintf(ap_amm_reg_name, 24, "ap_amm%" PRIu32, ap_num); + snprintf(ap_ac_reg_name, 24, "ap_ac%" PRIu32, ap_num); + CHECK_RETVAL(arc_set_register_value(target, ap_amv_reg_name, + match_value)); + CHECK_RETVAL(arc_set_register_value(target, ap_amm_reg_name, 0)); + CHECK_RETVAL(arc_set_register_value(target, ap_ac_reg_name, + control_tt | control_at)); + arc->actionpoints_num_avail--; + } else { + char ap_ac_reg_name[24]; + snprintf(ap_ac_reg_name, 24, "ap_ac%" PRIu32, ap_num); + CHECK_RETVAL(arc_set_register_value(target, ap_ac_reg_name, + AP_AC_TT_DISABLE)); + arc->actionpoints_num_avail++; + } + + return ERROR_OK; +} + +static int arc_set_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + if (breakpoint->is_set) { + LOG_WARNING("breakpoint already set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_SOFT) { + LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id); + + if (breakpoint->length == 4) { + uint32_t verify = 0xffffffff; + + CHECK_RETVAL(target_read_buffer(target, breakpoint->address, breakpoint->length, + breakpoint->orig_instr)); + + CHECK_RETVAL(arc_write_instruction_u32(target, breakpoint->address, + ARC_SDBBP_32)); + + CHECK_RETVAL(arc_read_instruction_u32(target, breakpoint->address, &verify)); + + if (verify != ARC_SDBBP_32) { + LOG_ERROR("Unable to set 32bit breakpoint at address @0x%" TARGET_PRIxADDR + " - check that memory is read/writable", breakpoint->address); + return ERROR_FAIL; + } + } else if (breakpoint->length == 2) { + uint16_t verify = 0xffff; + + CHECK_RETVAL(target_read_buffer(target, breakpoint->address, breakpoint->length, + breakpoint->orig_instr)); + CHECK_RETVAL(target_write_u16(target, breakpoint->address, ARC_SDBBP_16)); + + CHECK_RETVAL(target_read_u16(target, breakpoint->address, &verify)); + if (verify != ARC_SDBBP_16) { + LOG_ERROR("Unable to set 16bit breakpoint at address @0x%" TARGET_PRIxADDR + " - check that memory is read/writable", breakpoint->address); + return ERROR_FAIL; + } + } else { + LOG_ERROR("Invalid breakpoint length: target supports only 2 or 4"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + breakpoint->is_set = true; + } else if (breakpoint->type == BKPT_HARD) { + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + unsigned int bp_num; + + for (bp_num = 0; bp_num < arc->actionpoints_num; bp_num++) { + if (!ap_list[bp_num].used) + break; + } + + if (bp_num >= arc->actionpoints_num) { + LOG_ERROR("No free actionpoints, maximum amount is %u", + arc->actionpoints_num); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + int retval = arc_configure_actionpoint(target, bp_num, + breakpoint->address, AP_AC_TT_READWRITE, AP_AC_AT_INST_ADDR); + + if (retval == ERROR_OK) { + breakpoint_hw_set(breakpoint, bp_num); + ap_list[bp_num].used = 1; + ap_list[bp_num].bp_value = breakpoint->address; + ap_list[bp_num].type = ARC_AP_BREAKPOINT; + + LOG_DEBUG("bpid: %" PRIu32 ", bp_num %u bp_value 0x%" PRIx32, + breakpoint->unique_id, bp_num, ap_list[bp_num].bp_value); + } + + } else { + LOG_DEBUG("ERROR: setting unknown breakpoint type"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int arc_unset_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + int retval = ERROR_OK; + + if (!breakpoint->is_set) { + LOG_WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_SOFT) { + /* restore original instruction (kept in target endianness) */ + LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id); + if (breakpoint->length == 4) { + uint32_t current_instr; + + /* check that user program has not modified breakpoint instruction */ + CHECK_RETVAL(arc_read_instruction_u32(target, breakpoint->address, ¤t_instr)); + + if (current_instr == ARC_SDBBP_32) { + retval = target_write_buffer(target, breakpoint->address, + breakpoint->length, breakpoint->orig_instr); + if (retval != ERROR_OK) + return retval; + } else { + LOG_WARNING("Software breakpoint @0x%" TARGET_PRIxADDR + " has been overwritten outside of debugger." + "Expected: @0x%x, got: @0x%" PRIx32, + breakpoint->address, ARC_SDBBP_32, current_instr); + } + } else if (breakpoint->length == 2) { + uint16_t current_instr; + + /* check that user program has not modified breakpoint instruction */ + CHECK_RETVAL(target_read_u16(target, breakpoint->address, ¤t_instr)); + if (current_instr == ARC_SDBBP_16) { + retval = target_write_buffer(target, breakpoint->address, + breakpoint->length, breakpoint->orig_instr); + if (retval != ERROR_OK) + return retval; + } else { + LOG_WARNING("Software breakpoint @0x%" TARGET_PRIxADDR + " has been overwritten outside of debugger. " + "Expected: 0x%04x, got: 0x%04" PRIx16, + breakpoint->address, ARC_SDBBP_16, current_instr); + } + } else { + LOG_ERROR("Invalid breakpoint length: target supports only 2 or 4"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + breakpoint->is_set = false; + + } else if (breakpoint->type == BKPT_HARD) { + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + unsigned int bp_num = breakpoint->number; + + if (bp_num >= arc->actionpoints_num) { + LOG_DEBUG("Invalid actionpoint ID: %u in breakpoint: %" PRIu32, + bp_num, breakpoint->unique_id); + return ERROR_OK; + } + + retval = arc_configure_actionpoint(target, bp_num, + breakpoint->address, AP_AC_TT_DISABLE, AP_AC_AT_INST_ADDR); + + if (retval == ERROR_OK) { + breakpoint->is_set = false; + ap_list[bp_num].used = 0; + ap_list[bp_num].bp_value = 0; + + LOG_DEBUG("bpid: %" PRIu32 " - released actionpoint ID: %u", + breakpoint->unique_id, bp_num); + } + } else { + LOG_DEBUG("ERROR: unsetting unknown breakpoint type"); + return ERROR_FAIL; + } + + return retval; +} + +static int arc_enable_breakpoints(struct target *target) +{ + struct breakpoint *breakpoint = target->breakpoints; + + /* set any pending breakpoints */ + while (breakpoint) { + if (!breakpoint->is_set) + CHECK_RETVAL(arc_set_breakpoint(target, breakpoint)); + breakpoint = breakpoint->next; + } + + return ERROR_OK; +} + +static int arc_add_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + if (target->state == TARGET_HALTED) { + return arc_set_breakpoint(target, breakpoint); + + } else { + LOG_TARGET_ERROR(target, "not halted (add breakpoint)"); + return ERROR_TARGET_NOT_HALTED; + } +} + +static int arc_remove_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + if (target->state == TARGET_HALTED) { + if (breakpoint->is_set) + CHECK_RETVAL(arc_unset_breakpoint(target, breakpoint)); + } else { + LOG_TARGET_ERROR(target, "not halted (remove breakpoint)"); + return ERROR_TARGET_NOT_HALTED; + } + + return ERROR_OK; +} + +static void arc_reset_actionpoints(struct target *target) +{ + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + struct breakpoint *next_b; + struct watchpoint *next_w; + + while (target->breakpoints) { + next_b = target->breakpoints->next; + arc_remove_breakpoint(target, target->breakpoints); + free(target->breakpoints->orig_instr); + free(target->breakpoints); + target->breakpoints = next_b; + } + while (target->watchpoints) { + next_w = target->watchpoints->next; + arc_remove_watchpoint(target, target->watchpoints); + free(target->watchpoints); + target->watchpoints = next_w; + } + for (unsigned int i = 0; i < arc->actionpoints_num; i++) { + if ((ap_list[i].used) && (ap_list[i].reg_address)) + arc_remove_auxreg_actionpoint(target, ap_list[i].reg_address); + } +} + +int arc_set_actionpoints_num(struct target *target, uint32_t ap_num) +{ + LOG_DEBUG("target=%s actionpoints=%" PRIu32, target_name(target), ap_num); + struct arc_common *arc = target_to_arc(target); + + /* Make sure that there are no enabled actionpoints in target. */ + arc_reset_actionpoints(target); + + /* Assume that all points have been removed from target. */ + free(arc->actionpoints_list); + + arc->actionpoints_num_avail = ap_num; + arc->actionpoints_num = ap_num; + /* calloc can be safely called when ncount == 0. */ + arc->actionpoints_list = calloc(ap_num, sizeof(struct arc_actionpoint)); + + if (!arc->actionpoints_list) { + LOG_ERROR("Unable to allocate memory"); + return ERROR_FAIL; + } + return ERROR_OK; +} + + +int arc_add_auxreg_actionpoint(struct target *target, + uint32_t auxreg_addr, uint32_t transaction) +{ + unsigned int ap_num = 0; + int retval = ERROR_OK; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + + while (ap_list[ap_num].used) + ap_num++; + + if (ap_num >= arc->actionpoints_num) { + LOG_ERROR("No actionpoint free, maximum amount is %u", + arc->actionpoints_num); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = arc_configure_actionpoint(target, ap_num, + auxreg_addr, transaction, AP_AC_AT_AUXREG_ADDR); + + if (retval == ERROR_OK) { + ap_list[ap_num].used = 1; + ap_list[ap_num].reg_address = auxreg_addr; + } + + return retval; +} + +int arc_remove_auxreg_actionpoint(struct target *target, uint32_t auxreg_addr) +{ + int retval = ERROR_OK; + bool ap_found = false; + unsigned int ap_num = 0; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + + while ((ap_list[ap_num].used) && (ap_num < arc->actionpoints_num)) { + if (ap_list[ap_num].reg_address == auxreg_addr) { + ap_found = true; + break; + } + ap_num++; + } + + if (ap_found) { + retval = arc_configure_actionpoint(target, ap_num, + auxreg_addr, AP_AC_TT_DISABLE, AP_AC_AT_AUXREG_ADDR); + + if (retval == ERROR_OK) { + ap_list[ap_num].used = 0; + ap_list[ap_num].bp_value = 0; + } + } else { + LOG_ERROR("Register actionpoint not found"); + } + return retval; +} + + +static int arc_set_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + unsigned int wp_num; + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + + if (watchpoint->is_set) { + LOG_WARNING("watchpoint already set"); + return ERROR_OK; + } + + for (wp_num = 0; wp_num < arc->actionpoints_num; wp_num++) { + if (!ap_list[wp_num].used) + break; + } + + if (wp_num >= arc->actionpoints_num) { + LOG_ERROR("No free actionpoints, maximum amount is %u", + arc->actionpoints_num); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (watchpoint->length != 4) { + LOG_ERROR("Only watchpoints of length 4 are supported"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + int enable = AP_AC_TT_DISABLE; + switch (watchpoint->rw) { + case WPT_READ: + enable = AP_AC_TT_READ; + break; + case WPT_WRITE: + enable = AP_AC_TT_WRITE; + break; + case WPT_ACCESS: + enable = AP_AC_TT_READWRITE; + break; + default: + LOG_ERROR("BUG: watchpoint->rw neither read, write nor access"); + return ERROR_FAIL; + } + + int retval = arc_configure_actionpoint(target, wp_num, + watchpoint->address, enable, AP_AC_AT_MEMORY_ADDR); + + if (retval == ERROR_OK) { + watchpoint_set(watchpoint, wp_num); + ap_list[wp_num].used = 1; + ap_list[wp_num].bp_value = watchpoint->address; + ap_list[wp_num].type = ARC_AP_WATCHPOINT; + + LOG_DEBUG("wpid: %" PRIu32 ", wp_num %u wp_value 0x%" PRIx32, + watchpoint->unique_id, wp_num, ap_list[wp_num].bp_value); + } + + return retval; +} + +static int arc_unset_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + /* get pointers to arch-specific information */ + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + + if (!watchpoint->is_set) { + LOG_WARNING("watchpoint not set"); + return ERROR_OK; + } + + unsigned int wp_num = watchpoint->number; + if (wp_num >= arc->actionpoints_num) { + LOG_DEBUG("Invalid actionpoint ID: %u in watchpoint: %" PRIu32, + wp_num, watchpoint->unique_id); + return ERROR_OK; + } + + int retval = arc_configure_actionpoint(target, wp_num, + watchpoint->address, AP_AC_TT_DISABLE, AP_AC_AT_MEMORY_ADDR); + + if (retval == ERROR_OK) { + watchpoint->is_set = false; + ap_list[wp_num].used = 0; + ap_list[wp_num].bp_value = 0; + + LOG_DEBUG("wpid: %" PRIu32 " - releasing actionpoint ID: %u", + watchpoint->unique_id, wp_num); + } + + return retval; +} + +static int arc_enable_watchpoints(struct target *target) +{ + struct watchpoint *watchpoint = target->watchpoints; + + /* set any pending watchpoints */ + while (watchpoint) { + if (!watchpoint->is_set) + CHECK_RETVAL(arc_set_watchpoint(target, watchpoint)); + watchpoint = watchpoint->next; + } + + return ERROR_OK; +} + +static int arc_add_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "not halted"); + return ERROR_TARGET_NOT_HALTED; + } -/* Helper function which swiches core to single_step mode by + CHECK_RETVAL(arc_set_watchpoint(target, watchpoint)); + + return ERROR_OK; +} + +static int arc_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (watchpoint->is_set) + CHECK_RETVAL(arc_unset_watchpoint(target, watchpoint)); + + return ERROR_OK; +} + +static int arc_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) +{ + assert(target); + assert(hit_watchpoint); + + struct arc_actionpoint *actionpoint = NULL; + CHECK_RETVAL(get_current_actionpoint(target, &actionpoint)); + + if (actionpoint) { + if (!actionpoint->used) + LOG_WARNING("Target halted by unused actionpoint."); + + /* If this check fails - that is some sort of an error in OpenOCD. */ + if (actionpoint->type != ARC_AP_WATCHPOINT) + LOG_WARNING("Target halted by breakpoint, but is treated as a watchpoint."); + + for (struct watchpoint *watchpoint = target->watchpoints; + watchpoint; + watchpoint = watchpoint->next) { + if (actionpoint->bp_value == watchpoint->address) { + *hit_watchpoint = watchpoint; + LOG_DEBUG("Hit watchpoint, wpid: %" PRIu32 ", watchpoint num: %u", + watchpoint->unique_id, watchpoint->number); + return ERROR_OK; + } + } + } + + return ERROR_FAIL; +} + +/* Helper function which switches core to single_step mode by * doing aux r/w operations. */ -int arc_config_step(struct target *target, int enable_step) +static int arc_config_step(struct target *target, int enable_step) { uint32_t value; @@ -1324,7 +2068,23 @@ int arc_config_step(struct target *target, int enable_step) return ERROR_OK; } -int arc_step(struct target *target, int current, target_addr_t address, +static int arc_single_step_core(struct target *target) +{ + CHECK_RETVAL(arc_debug_entry(target)); + + /* disable interrupts while stepping */ + CHECK_RETVAL(arc_enable_interrupts(target, 0)); + + /* configure single step mode */ + CHECK_RETVAL(arc_config_step(target, 1)); + + /* exit debug mode */ + CHECK_RETVAL(arc_exit_debug(target)); + + return ERROR_OK; +} + +static int arc_step(struct target *target, int current, target_addr_t address, int handle_breakpoints) { /* get pointers to arch-specific information */ @@ -1333,15 +2093,15 @@ int arc_step(struct target *target, int current, target_addr_t address, struct reg *pc = &(arc->core_and_aux_cache->reg_list[arc->pc_index_in_cache]); if (target->state != TARGET_HALTED) { - LOG_WARNING("target not halted"); + LOG_TARGET_ERROR(target, "not halted"); return ERROR_TARGET_NOT_HALTED; } /* current = 1: continue on current pc, otherwise continue at
*/ if (!current) { buf_set_u32(pc->value, 0, 32, address); - pc->dirty = 1; - pc->valid = 1; + pc->dirty = true; + pc->valid = true; } LOG_DEBUG("Target steps one instruction from PC=0x%" PRIx32, @@ -1387,6 +2147,178 @@ int arc_step(struct target *target, int current, target_addr_t address, return ERROR_OK; } + +/* This function invalidates icache. */ +static int arc_icache_invalidate(struct target *target) +{ + uint32_t value; + + struct arc_common *arc = target_to_arc(target); + + /* Don't waste time if already done. */ + if (!arc->has_icache || arc->icache_invalidated) + return ERROR_OK; + + LOG_DEBUG("Invalidating I$."); + + value = IC_IVIC_INVALIDATE; /* invalidate I$ */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_IC_IVIC_REG, value)); + + arc->icache_invalidated = true; + + return ERROR_OK; +} + +/* This function invalidates dcache */ +static int arc_dcache_invalidate(struct target *target) +{ + uint32_t value, dc_ctrl_value; + + struct arc_common *arc = target_to_arc(target); + + if (!arc->has_dcache || arc->dcache_invalidated) + return ERROR_OK; + + LOG_DEBUG("Invalidating D$."); + + CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, &value)); + dc_ctrl_value = value; + value &= ~DC_CTRL_IM; + + /* set DC_CTRL invalidate mode to invalidate-only (no flushing!!) */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, value)); + value = DC_IVDC_INVALIDATE; /* invalidate D$ */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_IVDC_REG, value)); + + /* restore DC_CTRL invalidate mode */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, dc_ctrl_value)); + + arc->dcache_invalidated = true; + + return ERROR_OK; +} + +/* This function invalidates l2 cache. */ +static int arc_l2cache_invalidate(struct target *target) +{ + uint32_t value, slc_ctrl_value; + + struct arc_common *arc = target_to_arc(target); + + if (!arc->has_l2cache || arc->l2cache_invalidated) + return ERROR_OK; + + LOG_DEBUG("Invalidating L2$."); + + CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, &value)); + slc_ctrl_value = value; + value &= ~L2_CTRL_IM; + + /* set L2_CTRL invalidate mode to invalidate-only (no flushing!!) */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, value)); + /* invalidate L2$ */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_INV, L2_INV_IV)); + + /* Wait until invalidate operation ends */ + do { + LOG_DEBUG("Waiting for invalidation end."); + CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, &value)); + } while (value & L2_CTRL_BS); + + /* restore L2_CTRL invalidate mode */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, slc_ctrl_value)); + + arc->l2cache_invalidated = true; + + return ERROR_OK; +} + + +int arc_cache_invalidate(struct target *target) +{ + CHECK_RETVAL(arc_icache_invalidate(target)); + CHECK_RETVAL(arc_dcache_invalidate(target)); + CHECK_RETVAL(arc_l2cache_invalidate(target)); + + return ERROR_OK; +} + +/* Flush data cache. This function is cheap to call and return quickly if D$ + * already has been flushed since target had been halted. JTAG debugger reads + * values directly from memory, bypassing cache, so if there are unflushed + * lines debugger will read invalid values, which will cause a lot of troubles. + * */ +static int arc_dcache_flush(struct target *target) +{ + uint32_t value, dc_ctrl_value; + bool has_to_set_dc_ctrl_im; + + struct arc_common *arc = target_to_arc(target); + + /* Don't waste time if already done. */ + if (!arc->has_dcache || arc->dcache_flushed) + return ERROR_OK; + + LOG_DEBUG("Flushing D$."); + + /* Store current value of DC_CTRL */ + CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, &dc_ctrl_value)); + + /* Set DC_CTRL invalidate mode to flush (if not already set) */ + has_to_set_dc_ctrl_im = (dc_ctrl_value & DC_CTRL_IM) == 0; + if (has_to_set_dc_ctrl_im) { + value = dc_ctrl_value | DC_CTRL_IM; + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, value)); + } + + /* Flush D$ */ + value = DC_IVDC_INVALIDATE; + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_IVDC_REG, value)); + + /* Restore DC_CTRL invalidate mode (even of flush failed) */ + if (has_to_set_dc_ctrl_im) + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, dc_ctrl_value)); + + arc->dcache_flushed = true; + + return ERROR_OK; +} + +/* This function flushes l2cache. */ +static int arc_l2cache_flush(struct target *target) +{ + uint32_t value; + + struct arc_common *arc = target_to_arc(target); + + /* Don't waste time if already done. */ + if (!arc->has_l2cache || arc->l2cache_flushed) + return ERROR_OK; + + LOG_DEBUG("Flushing L2$."); + + /* Flush L2 cache */ + CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_FLUSH, L2_FLUSH_FL)); + + /* Wait until flush operation ends */ + do { + LOG_DEBUG("Waiting for flushing end."); + CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, &value)); + } while (value & L2_CTRL_BS); + + arc->l2cache_flushed = true; + + return ERROR_OK; +} + +int arc_cache_flush(struct target *target) +{ + CHECK_RETVAL(arc_dcache_flush(target)); + CHECK_RETVAL(arc_l2cache_flush(target)); + + return ERROR_OK; +} + /* ARC v2 target */ struct target_type arcv2_target = { .name = "arcv2", @@ -1395,7 +2327,7 @@ struct target_type arcv2_target = { .arch_state = arc_arch_state, - /* TODO That seems like something similiar to metaware hostlink, so perhaps + /* TODO That seems like something similar to metaware hostlink, so perhaps * we can exploit this in the future. */ .target_request_data = NULL, @@ -1416,13 +2348,13 @@ struct target_type arcv2_target = { .checksum_memory = NULL, .blank_check_memory = NULL, - .add_breakpoint = NULL, + .add_breakpoint = arc_add_breakpoint, .add_context_breakpoint = NULL, .add_hybrid_breakpoint = NULL, - .remove_breakpoint = NULL, - .add_watchpoint = NULL, - .remove_watchpoint = NULL, - .hit_watchpoint = NULL, + .remove_breakpoint = arc_remove_breakpoint, + .add_watchpoint = arc_add_watchpoint, + .remove_watchpoint = arc_remove_watchpoint, + .hit_watchpoint = arc_hit_watchpoint, .run_algorithm = NULL, .start_algorithm = NULL,