+
+ /* reset our timeout */
+ timeout = 0;
+
+ /* Limit to the amount of data we actually want to write */
+ if (thisrun_bytes > count * block_size)
+ thisrun_bytes = count * block_size;
+
+ /* Force end of large blocks to be word aligned */
+ if (thisrun_bytes >= 16)
+ thisrun_bytes -= (rp + thisrun_bytes) & 0x03;
+
+ /* Write data to fifo */
+ retval = target_write_buffer(target, wp, thisrun_bytes, buffer);
+ if (retval != ERROR_OK)
+ break;
+
+ /* Update counters and wrap write pointer */
+ buffer += thisrun_bytes;
+ count -= thisrun_bytes / block_size;
+ wp += thisrun_bytes;
+ if (wp >= fifo_end_addr)
+ wp = fifo_start_addr;
+
+ /* Store updated write pointer to target */
+ retval = target_write_u32(target, wp_addr, wp);
+ if (retval != ERROR_OK)
+ break;
+
+ /* Avoid GDB timeouts */
+ keep_alive();
+ }
+
+ if (retval != ERROR_OK) {
+ /* abort flash write algorithm on target */
+ target_write_u32(target, wp_addr, 0);
+ }
+
+ int retval2 = target_wait_algorithm(target, num_mem_params, mem_params,
+ num_reg_params, reg_params,
+ exit_point,
+ 10000,
+ arch_info);
+
+ if (retval2 != ERROR_OK) {
+ LOG_ERROR("error waiting for target flash write algorithm");
+ retval = retval2;
+ }
+
+ if (retval == ERROR_OK) {
+ /* check if algorithm set rp = 0 after fifo writer loop finished */
+ retval = target_read_u32(target, rp_addr, &rp);
+ if (retval == ERROR_OK && rp == 0) {
+ LOG_ERROR("flash write algorithm aborted by target");
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ return retval;
+}
+
+int target_run_read_async_algorithm(struct target *target,
+ uint8_t *buffer, uint32_t count, int block_size,
+ int num_mem_params, struct mem_param *mem_params,
+ int num_reg_params, struct reg_param *reg_params,
+ uint32_t buffer_start, uint32_t buffer_size,
+ uint32_t entry_point, uint32_t exit_point, void *arch_info)
+{
+ int retval;
+ int timeout = 0;
+
+ const uint8_t *buffer_orig = buffer;
+
+ /* Set up working area. First word is write pointer, second word is read pointer,
+ * rest is fifo data area. */
+ uint32_t wp_addr = buffer_start;
+ uint32_t rp_addr = buffer_start + 4;
+ uint32_t fifo_start_addr = buffer_start + 8;
+ uint32_t fifo_end_addr = buffer_start + buffer_size;
+
+ uint32_t wp = fifo_start_addr;
+ uint32_t rp = fifo_start_addr;
+
+ /* validate block_size is 2^n */
+ assert(IS_PWR_OF_2(block_size));
+
+ retval = target_write_u32(target, wp_addr, wp);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u32(target, rp_addr, rp);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Start up algorithm on target */
+ retval = target_start_algorithm(target, num_mem_params, mem_params,
+ num_reg_params, reg_params,
+ entry_point,
+ exit_point,
+ arch_info);
+
+ if (retval != ERROR_OK) {
+ LOG_ERROR("error starting target flash read algorithm");
+ return retval;
+ }
+
+ while (count > 0) {
+ retval = target_read_u32(target, wp_addr, &wp);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("failed to get write pointer");
+ break;
+ }
+
+ LOG_DEBUG("offs 0x%zx count 0x%" PRIx32 " wp 0x%" PRIx32 " rp 0x%" PRIx32,
+ (size_t)(buffer - buffer_orig), count, wp, rp);
+
+ if (wp == 0) {
+ LOG_ERROR("flash read algorithm aborted by target");
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ break;
+ }
+
+ if (!IS_ALIGNED(wp - fifo_start_addr, block_size) || wp < fifo_start_addr || wp >= fifo_end_addr) {
+ LOG_ERROR("corrupted fifo write pointer 0x%" PRIx32, wp);
+ break;
+ }
+
+ /* Count the number of bytes available in the fifo without
+ * crossing the wrap around. */
+ uint32_t thisrun_bytes;
+ if (wp >= rp)
+ thisrun_bytes = wp - rp;
+ else
+ thisrun_bytes = fifo_end_addr - rp;
+
+ if (thisrun_bytes == 0) {
+ /* Throttle polling a bit if transfer is (much) faster than flash
+ * reading. The exact delay shouldn't matter as long as it's
+ * less than buffer size / flash speed. This is very unlikely to
+ * run when using high latency connections such as USB. */
+ alive_sleep(2);
+
+ /* to stop an infinite loop on some targets check and increment a timeout
+ * this issue was observed on a stellaris using the new ICDI interface */
+ if (timeout++ >= 2500) {
+ LOG_ERROR("timeout waiting for algorithm, a target reset is recommended");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ continue;
+ }
+
+ /* Reset our timeout */
+ timeout = 0;
+
+ /* Limit to the amount of data we actually want to read */
+ if (thisrun_bytes > count * block_size)
+ thisrun_bytes = count * block_size;
+
+ /* Force end of large blocks to be word aligned */
+ if (thisrun_bytes >= 16)
+ thisrun_bytes -= (rp + thisrun_bytes) & 0x03;
+
+ /* Read data from fifo */
+ retval = target_read_buffer(target, rp, thisrun_bytes, buffer);
+ if (retval != ERROR_OK)
+ break;
+
+ /* Update counters and wrap write pointer */
+ buffer += thisrun_bytes;
+ count -= thisrun_bytes / block_size;
+ rp += thisrun_bytes;
+ if (rp >= fifo_end_addr)
+ rp = fifo_start_addr;
+
+ /* Store updated write pointer to target */
+ retval = target_write_u32(target, rp_addr, rp);
+ if (retval != ERROR_OK)
+ break;
+
+ /* Avoid GDB timeouts */
+ keep_alive();
+
+ }
+
+ if (retval != ERROR_OK) {
+ /* abort flash write algorithm on target */
+ target_write_u32(target, rp_addr, 0);
+ }
+
+ int retval2 = target_wait_algorithm(target, num_mem_params, mem_params,
+ num_reg_params, reg_params,
+ exit_point,
+ 10000,
+ arch_info);
+
+ if (retval2 != ERROR_OK) {
+ LOG_ERROR("error waiting for target flash write algorithm");
+ retval = retval2;
+ }
+
+ if (retval == ERROR_OK) {
+ /* check if algorithm set wp = 0 after fifo writer loop finished */
+ retval = target_read_u32(target, wp_addr, &wp);
+ if (retval == ERROR_OK && wp == 0) {
+ LOG_ERROR("flash read algorithm aborted by target");
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ return retval;
+}
+
+int target_read_memory(struct target *target,
+ target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ if (!target_was_examined(target)) {
+ LOG_ERROR("Target not examined yet");
+ return ERROR_FAIL;
+ }
+ if (!target->type->read_memory) {
+ LOG_ERROR("Target %s doesn't support read_memory", target_name(target));
+ return ERROR_FAIL;
+ }
+ return target->type->read_memory(target, address, size, count, buffer);
+}
+
+int target_read_phys_memory(struct target *target,
+ target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ if (!target_was_examined(target)) {
+ LOG_ERROR("Target not examined yet");
+ return ERROR_FAIL;
+ }
+ if (!target->type->read_phys_memory) {
+ LOG_ERROR("Target %s doesn't support read_phys_memory", target_name(target));
+ return ERROR_FAIL;
+ }
+ return target->type->read_phys_memory(target, address, size, count, buffer);
+}
+
+int target_write_memory(struct target *target,
+ target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ if (!target_was_examined(target)) {
+ LOG_ERROR("Target not examined yet");
+ return ERROR_FAIL;
+ }
+ if (!target->type->write_memory) {
+ LOG_ERROR("Target %s doesn't support write_memory", target_name(target));
+ return ERROR_FAIL;
+ }
+ return target->type->write_memory(target, address, size, count, buffer);
+}
+
+int target_write_phys_memory(struct target *target,
+ target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ if (!target_was_examined(target)) {
+ LOG_ERROR("Target not examined yet");
+ return ERROR_FAIL;
+ }
+ if (!target->type->write_phys_memory) {
+ LOG_ERROR("Target %s doesn't support write_phys_memory", target_name(target));
+ return ERROR_FAIL;
+ }
+ return target->type->write_phys_memory(target, address, size, count, buffer);
+}
+
+int target_add_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ if ((target->state != TARGET_HALTED) && (breakpoint->type != BKPT_HARD)) {
+ LOG_WARNING("target %s is not halted (add breakpoint)", target_name(target));
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->add_breakpoint(target, breakpoint);
+}
+
+int target_add_context_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted (add context breakpoint)", target_name(target));
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->add_context_breakpoint(target, breakpoint);
+}
+
+int target_add_hybrid_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted (add hybrid breakpoint)", target_name(target));
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->add_hybrid_breakpoint(target, breakpoint);
+}
+
+int target_remove_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ return target->type->remove_breakpoint(target, breakpoint);
+}
+
+int target_add_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted (add watchpoint)", target_name(target));
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->add_watchpoint(target, watchpoint);
+}
+int target_remove_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ return target->type->remove_watchpoint(target, watchpoint);
+}
+int target_hit_watchpoint(struct target *target,
+ struct watchpoint **hit_watchpoint)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted (hit watchpoint)", target->cmd_name);
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (!target->type->hit_watchpoint) {
+ /* For backward compatible, if hit_watchpoint is not implemented,
+ * return ERROR_FAIL such that gdb_server will not take the nonsense
+ * information. */
+ return ERROR_FAIL;
+ }
+
+ return target->type->hit_watchpoint(target, hit_watchpoint);
+}
+
+const char *target_get_gdb_arch(struct target *target)
+{
+ if (!target->type->get_gdb_arch)
+ return NULL;
+ return target->type->get_gdb_arch(target);
+}
+
+int target_get_gdb_reg_list(struct target *target,
+ struct reg **reg_list[], int *reg_list_size,
+ enum target_register_class reg_class)
+{
+ int result = ERROR_FAIL;
+
+ if (!target_was_examined(target)) {
+ LOG_ERROR("Target not examined yet");
+ goto done;
+ }
+
+ result = target->type->get_gdb_reg_list(target, reg_list,
+ reg_list_size, reg_class);
+
+done:
+ if (result != ERROR_OK) {
+ *reg_list = NULL;
+ *reg_list_size = 0;
+ }
+ return result;
+}
+
+int target_get_gdb_reg_list_noread(struct target *target,
+ struct reg **reg_list[], int *reg_list_size,
+ enum target_register_class reg_class)
+{
+ if (target->type->get_gdb_reg_list_noread &&
+ target->type->get_gdb_reg_list_noread(target, reg_list,
+ reg_list_size, reg_class) == ERROR_OK)
+ return ERROR_OK;
+ return target_get_gdb_reg_list(target, reg_list, reg_list_size, reg_class);
+}
+
+bool target_supports_gdb_connection(struct target *target)
+{
+ /*
+ * exclude all the targets that don't provide get_gdb_reg_list
+ * or that have explicit gdb_max_connection == 0
+ */
+ return !!target->type->get_gdb_reg_list && !!target->gdb_max_connections;
+}
+
+int target_step(struct target *target,
+ int current, target_addr_t address, int handle_breakpoints)
+{
+ int retval;
+
+ target_call_event_callbacks(target, TARGET_EVENT_STEP_START);
+
+ retval = target->type->step(target, current, address, handle_breakpoints);
+ if (retval != ERROR_OK)
+ return retval;
+
+ target_call_event_callbacks(target, TARGET_EVENT_STEP_END);
+
+ return retval;
+}
+
+int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted (gdb fileio)", target->cmd_name);
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->get_gdb_fileio_info(target, fileio_info);
+}
+
+int target_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted (gdb fileio end)", target->cmd_name);
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->gdb_fileio_end(target, retcode, fileio_errno, ctrl_c);
+}
+
+target_addr_t target_address_max(struct target *target)
+{
+ unsigned bits = target_address_bits(target);
+ if (sizeof(target_addr_t) * 8 == bits)
+ return (target_addr_t) -1;
+ else
+ return (((target_addr_t) 1) << bits) - 1;
+}
+
+unsigned target_address_bits(struct target *target)
+{
+ if (target->type->address_bits)
+ return target->type->address_bits(target);
+ return 32;
+}
+
+unsigned int target_data_bits(struct target *target)
+{
+ if (target->type->data_bits)
+ return target->type->data_bits(target);
+ return 32;
+}
+
+static int target_profiling(struct target *target, uint32_t *samples,
+ uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
+{
+ return target->type->profiling(target, samples, max_num_samples,
+ num_samples, seconds);
+}
+
+static int handle_target(void *priv);
+
+static int target_init_one(struct command_context *cmd_ctx,
+ struct target *target)
+{
+ target_reset_examined(target);
+
+ struct target_type *type = target->type;
+ if (!type->examine)
+ type->examine = default_examine;
+
+ if (!type->check_reset)
+ type->check_reset = default_check_reset;
+
+ assert(type->init_target);
+
+ int retval = type->init_target(cmd_ctx, target);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("target '%s' init failed", target_name(target));
+ return retval;
+ }
+
+ /* Sanity-check MMU support ... stub in what we must, to help
+ * implement it in stages, but warn if we need to do so.
+ */
+ if (type->mmu) {
+ if (!type->virt2phys) {
+ LOG_ERROR("type '%s' is missing virt2phys", type->name);
+ type->virt2phys = identity_virt2phys;
+ }
+ } else {
+ /* Make sure no-MMU targets all behave the same: make no