From 8b4e882a1630d63bbc9840fa3f968e36b6ac3702 Mon Sep 17 00:00:00 2001 From: drath Date: Fri, 2 Jun 2006 10:36:31 +0000 Subject: - prepare OpenOCD for branching, created ./trunk/ git-svn-id: svn://svn.berlios.de/openocd/trunk@64 b42882b7-edfa-0310-969c-e2dbd0fdcd60 --- src/target/target.c | 1701 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1701 insertions(+) create mode 100644 src/target/target.c (limited to 'src/target/target.c') diff --git a/src/target/target.c b/src/target/target.c new file mode 100644 index 00000000..98407afb --- /dev/null +++ b/src/target/target.c @@ -0,0 +1,1701 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#include "target.h" + +#include "log.h" +#include "configuration.h" +#include "binarybuffer.h" +#include "jtag.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +int cli_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv); + +int handle_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_targets_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int handle_target_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_run_and_halt_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_working_area_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int handle_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_poll_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_wait_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_reset_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_soft_reset_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_resume_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_step_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_md_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_mw_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_load_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_dump_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_bp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_rbp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_wp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_rwp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* targets + */ +extern target_type_t arm7tdmi_target; +extern target_type_t arm720t_target; +extern target_type_t arm9tdmi_target; +extern target_type_t arm920t_target; + +target_type_t *target_types[] = +{ + &arm7tdmi_target, + &arm9tdmi_target, + &arm920t_target, + &arm720t_target, + NULL, +}; + +target_t *targets = NULL; +target_event_callback_t *target_event_callbacks = NULL; +target_timer_callback_t *target_timer_callbacks = NULL; + +char *target_state_strings[] = +{ + "unknown", + "running", + "halted", + "reset", + "debug_running", +}; + +char *target_debug_reason_strings[] = +{ + "debug request", "breakpoint", "watchpoint", + "watchpoint and breakpoint", "single step", + "target not halted" +}; + +char *target_endianess_strings[] = +{ + "big endian", + "little endian", +}; + +enum daemon_startup_mode startup_mode = DAEMON_ATTACH; + +static int target_continous_poll = 1; + +/* returns a pointer to the n-th configured target */ +target_t* get_target_by_num(int num) +{ + target_t *target = targets; + int i = 0; + + while (target) + { + if (num == i) + return target; + target = target->next; + i++; + } + + return NULL; +} + +int get_num_by_target(target_t *query_target) +{ + target_t *target = targets; + int i = 0; + + while (target) + { + if (target == query_target) + return i; + target = target->next; + i++; + } + + return -1; +} + +target_t* get_current_target(command_context_t *cmd_ctx) +{ + target_t *target = get_target_by_num(cmd_ctx->current_target); + + if (target == NULL) + { + ERROR("BUG: current_target out of bounds"); + exit(-1); + } + + return target; +} + +/* Process target initialization, when target entered debug out of reset + * the handler is unregistered at the end of this function, so it's only called once + */ +int target_init_handler(struct target_s *target, enum target_event event, void *priv) +{ + FILE *script; + struct command_context_s *cmd_ctx = priv; + + if ((event == TARGET_EVENT_HALTED) && (target->reset_script)) + { + script = fopen(target->reset_script, "r"); + if (!script) + { + ERROR("couldn't open script file %s", target->reset_script); + return ERROR_OK; + } + + INFO("executing reset script '%s'", target->reset_script); + command_run_file(cmd_ctx, script, COMMAND_EXEC); + fclose(script); + + jtag_execute_queue(); + + target_unregister_event_callback(target_init_handler, priv); + } + + return ERROR_OK; +} + +int target_run_and_halt_handler(void *priv) +{ + target_t *target = priv; + + target->type->halt(target); + + return ERROR_OK; +} + +int target_process_reset(struct command_context_s *cmd_ctx) +{ + int retval = ERROR_OK; + target_t *target; + + target = targets; + while (target) + { + target->type->assert_reset(target); + target = target->next; + } + jtag_execute_queue(); + + /* request target halt if necessary, and schedule further action */ + target = targets; + while (target) + { + switch (target->reset_mode) + { + case RESET_RUN: + /* nothing to do if target just wants to be run */ + break; + case RESET_RUN_AND_HALT: + /* schedule halt */ + target_register_timer_callback(target_run_and_halt_handler, target->run_and_halt_time, 0, target); + break; + case RESET_RUN_AND_INIT: + /* schedule halt */ + target_register_timer_callback(target_run_and_halt_handler, target->run_and_halt_time, 0, target); + target_register_event_callback(target_init_handler, cmd_ctx); + break; + case RESET_HALT: + target->type->halt(target); + break; + case RESET_INIT: + target->type->halt(target); + target_register_event_callback(target_init_handler, cmd_ctx); + break; + default: + ERROR("BUG: unknown target->reset_mode"); + } + target = target->next; + } + + target = targets; + while (target) + { + target->type->deassert_reset(target); + target = target->next; + } + jtag_execute_queue(); + + return retval; +} + +int target_init(struct command_context_s *cmd_ctx) +{ + target_t *target = targets; + + while (target) + { + if (target->type->init_target(cmd_ctx, target) != ERROR_OK) + { + ERROR("target '%s' init failed", target->type->name); + exit(-1); + } + target = target->next; + } + + if (targets) + { + target_register_user_commands(cmd_ctx); + target_register_timer_callback(handle_target, 100, 1, NULL); + } + + if (startup_mode == DAEMON_RESET) + target_process_reset(cmd_ctx); + + return ERROR_OK; +} + +int target_register_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv) +{ + target_event_callback_t **callbacks_p = &target_event_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + if (*callbacks_p) + { + while ((*callbacks_p)->next) + callbacks_p = &((*callbacks_p)->next); + callbacks_p = &((*callbacks_p)->next); + } + + (*callbacks_p) = malloc(sizeof(target_event_callback_t)); + (*callbacks_p)->callback = callback; + (*callbacks_p)->priv = priv; + (*callbacks_p)->next = NULL; + + return ERROR_OK; +} + +int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv) +{ + target_timer_callback_t **callbacks_p = &target_timer_callbacks; + struct timeval now; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + if (*callbacks_p) + { + while ((*callbacks_p)->next) + callbacks_p = &((*callbacks_p)->next); + callbacks_p = &((*callbacks_p)->next); + } + + (*callbacks_p) = malloc(sizeof(target_timer_callback_t)); + (*callbacks_p)->callback = callback; + (*callbacks_p)->periodic = periodic; + (*callbacks_p)->time_ms = time_ms; + + gettimeofday(&now, NULL); + (*callbacks_p)->when.tv_usec = now.tv_usec + (time_ms % 1000) * 1000; + time_ms -= (time_ms % 1000); + (*callbacks_p)->when.tv_sec = now.tv_sec + (time_ms / 1000); + if ((*callbacks_p)->when.tv_usec > 1000000) + { + (*callbacks_p)->when.tv_usec = (*callbacks_p)->when.tv_usec - 1000000; + (*callbacks_p)->when.tv_sec += 1; + } + + (*callbacks_p)->priv = priv; + (*callbacks_p)->next = NULL; + + return ERROR_OK; +} + +int target_unregister_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv) +{ + target_event_callback_t **p = &target_event_callbacks; + target_event_callback_t *c = target_event_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + while (c) + { + target_event_callback_t *next = c->next; + if ((c->callback == callback) && (c->priv == priv)) + { + *p = next; + free(c); + return ERROR_OK; + } + else + p = &(c->next); + c = next; + } + + return ERROR_OK; +} + +int target_unregister_timer_callback(int (*callback)(void *priv), void *priv) +{ + target_timer_callback_t **p = &target_timer_callbacks; + target_timer_callback_t *c = target_timer_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + while (c) + { + target_timer_callback_t *next = c->next; + if ((c->callback == callback) && (c->priv == priv)) + { + *p = next; + free(c); + return ERROR_OK; + } + else + p = &(c->next); + c = next; + } + + return ERROR_OK; +} + +int target_call_event_callbacks(target_t *target, enum target_event event) +{ + target_event_callback_t *callback = target_event_callbacks; + target_event_callback_t *next_callback; + + DEBUG("target event %i", event); + + while (callback) + { + next_callback = callback->next; + callback->callback(target, event, callback->priv); + callback = next_callback; + } + + return ERROR_OK; +} + +int target_call_timer_callbacks() +{ + target_timer_callback_t *callback = target_timer_callbacks; + target_timer_callback_t *next_callback; + struct timeval now; + + gettimeofday(&now, NULL); + + while (callback) + { + next_callback = callback->next; + + if (((now.tv_sec >= callback->when.tv_sec) && (now.tv_usec >= callback->when.tv_usec)) + || (now.tv_sec > callback->when.tv_sec)) + { + callback->callback(callback->priv); + if (callback->periodic) + { + int time_ms = callback->time_ms; + callback->when.tv_usec = now.tv_usec + (time_ms % 1000) * 1000; + time_ms -= (time_ms % 1000); + callback->when.tv_sec = now.tv_sec + time_ms / 1000; + if (callback->when.tv_usec > 1000000) + { + callback->when.tv_usec = callback->when.tv_usec - 1000000; + callback->when.tv_sec += 1; + } + } + else + target_unregister_timer_callback(callback->callback, callback->priv); + } + + callback = next_callback; + } + + return ERROR_OK; +} + +int target_alloc_working_area(struct target_s *target, u32 size, working_area_t **area) +{ + working_area_t *c = target->working_areas; + working_area_t *new_wa = NULL; + + /* only allocate multiples of 4 byte */ + if (size % 4) + { + ERROR("BUG: code tried to allocate unaligned number of bytes, padding"); + size = CEIL(size, 4); + } + + /* see if there's already a matching working area */ + while (c) + { + if ((c->free) && (c->size == size)) + { + new_wa = c; + break; + } + c = c->next; + } + + /* if not, allocate a new one */ + if (!new_wa) + { + working_area_t **p = &target->working_areas; + u32 first_free = target->working_area; + u32 free_size = target->working_area_size; + + DEBUG("allocating new working area"); + + c = target->working_areas; + while (c) + { + first_free += c->size; + free_size -= c->size; + p = &c->next; + c = c->next; + } + + if (free_size < size) + { + WARNING("not enough working area available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + new_wa = malloc(sizeof(working_area_t)); + new_wa->next = NULL; + new_wa->size = size; + new_wa->address = first_free; + + if (target->backup_working_area) + { + new_wa->backup = malloc(new_wa->size); + target->type->read_memory(target, new_wa->address, 4, new_wa->size / 4, new_wa->backup); + } + else + { + new_wa->backup = NULL; + } + + /* put new entry in list */ + *p = new_wa; + } + + /* mark as used, and return the new (reused) area */ + new_wa->free = 0; + *area = new_wa; + + /* user pointer */ + new_wa->user = area; + + return ERROR_OK; +} + +int target_free_working_area(struct target_s *target, working_area_t *area) +{ + if (area->free) + return ERROR_OK; + + if (target->backup_working_area) + target->type->write_memory(target, area->address, 4, area->size / 4, area->backup); + + area->free = 1; + + /* mark user pointer invalid */ + *area->user = NULL; + area->user = NULL; + + return ERROR_OK; +} + +int target_free_all_working_areas(struct target_s *target) +{ + working_area_t *c = target->working_areas; + + while (c) + { + working_area_t *next = c->next; + target_free_working_area(target, c); + + if (c->backup) + free(c->backup); + + free(c); + + c = next; + } + + target->working_areas = NULL; + + return ERROR_OK; +} + +int target_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "target", handle_target_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "targets", handle_targets_command, COMMAND_EXEC, NULL); + register_command(cmd_ctx, NULL, "daemon_startup", handle_daemon_startup_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "target_script", handle_target_script_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "run_and_halt_time", handle_run_and_halt_time_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "working_area", handle_working_area_command, COMMAND_CONFIG, NULL); + + return ERROR_OK; +} + +int target_write_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer) +{ + int retval; + + DEBUG("writing buffer of %i byte at 0x%8.8x", size, address); + + /* handle writes of less than 4 byte */ + if (size < 4) + { + if ((retval = target->type->write_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + /* handle unaligned head bytes */ + if (address % 4) + { + int unaligned = 4 - (address % 4); + + if ((retval = target->type->write_memory(target, address, 1, unaligned, buffer)) != ERROR_OK) + return retval; + + buffer += unaligned; + address += unaligned; + size -= unaligned; + } + + /* handle aligned words */ + if (size >= 4) + { + int aligned = size - (size % 4); + + /* use bulk writes above a certain limit. This may have to be changed */ + if (aligned > 128) + { + if ((retval = target->type->bulk_write_memory(target, address, aligned / 4, buffer)) != ERROR_OK) + return retval; + } + else + { + if ((retval = target->type->write_memory(target, address, 4, aligned / 4, buffer)) != ERROR_OK) + return retval; + } + + buffer += aligned; + address += aligned; + size -= aligned; + } + + /* handle tail writes of less than 4 bytes */ + if (size > 0) + { + if ((retval = target->type->write_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +int target_read_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer) +{ + int retval; + + DEBUG("reading buffer of %i byte at 0x%8.8x", size, address); + + /* handle reads of less than 4 byte */ + if (size < 4) + { + if ((retval = target->type->read_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + /* handle unaligned head bytes */ + if (address % 4) + { + int unaligned = 4 - (address % 4); + + if ((retval = target->type->read_memory(target, address, 1, unaligned, buffer)) != ERROR_OK) + return retval; + + address += unaligned; + size -= unaligned; + } + + /* handle aligned words */ + if (size >= 4) + { + int aligned = size - (size % 4); + + if ((retval = target->type->read_memory(target, address, 4, aligned / 4, buffer)) != ERROR_OK) + return retval; + + address += aligned; + size -= aligned; + } + + /* handle tail writes of less than 4 bytes */ + if (size > 0) + { + if ((retval = target->type->read_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +int target_register_user_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "reg", handle_reg_command, COMMAND_EXEC, NULL); + register_command(cmd_ctx, NULL, "poll", handle_poll_command, COMMAND_EXEC, "poll target state"); + register_command(cmd_ctx, NULL, "wait_halt", handle_wait_halt_command, COMMAND_EXEC, "wait for target halt"); + register_command(cmd_ctx, NULL, "halt", handle_halt_command, COMMAND_EXEC, "halt target"); + register_command(cmd_ctx, NULL, "resume", handle_resume_command, COMMAND_EXEC, "resume target [addr]"); + register_command(cmd_ctx, NULL, "step", handle_step_command, COMMAND_EXEC, "step one instruction"); + register_command(cmd_ctx, NULL, "reset", handle_reset_command, COMMAND_EXEC, "reset target [run|halt|init|run_and_halt|run_and_init]"); + register_command(cmd_ctx, NULL, "soft_reset_halt", handle_soft_reset_halt_command, COMMAND_EXEC, "halt the target and do a soft reset"); + + register_command(cmd_ctx, NULL, "mdw", handle_md_command, COMMAND_EXEC, "display memory words [count]"); + register_command(cmd_ctx, NULL, "mdh", handle_md_command, COMMAND_EXEC, "display memory half-words [count]"); + register_command(cmd_ctx, NULL, "mdb", handle_md_command, COMMAND_EXEC, "display memory bytes [count]"); + + register_command(cmd_ctx, NULL, "mww", handle_mw_command, COMMAND_EXEC, "write memory word "); + register_command(cmd_ctx, NULL, "mwh", handle_mw_command, COMMAND_EXEC, "write memory half-word "); + register_command(cmd_ctx, NULL, "mwb", handle_mw_command, COMMAND_EXEC, "write memory byte "); + + register_command(cmd_ctx, NULL, "bp", handle_bp_command, COMMAND_EXEC, "set breakpoint
[hw]"); + register_command(cmd_ctx, NULL, "rbp", handle_rbp_command, COMMAND_EXEC, "remove breakpoint "); + register_command(cmd_ctx, NULL, "wp", handle_wp_command, COMMAND_EXEC, "set watchpoint
[value] [mask]"); + register_command(cmd_ctx, NULL, "rwp", handle_rwp_command, COMMAND_EXEC, "remove watchpoint "); + + register_command(cmd_ctx, NULL, "load_binary", handle_load_binary_command, COMMAND_EXEC, "load binary
"); + register_command(cmd_ctx, NULL, "dump_binary", handle_dump_binary_command, COMMAND_EXEC, "dump binary
"); + + return ERROR_OK; +} + +int handle_targets_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = targets; + int count = 0; + + if (argc == 1) + { + int num = strtoul(args[0], NULL, 0); + + while (target) + { + count++; + target = target->next; + } + + if (num < count) + cmd_ctx->current_target = num; + else + command_print(cmd_ctx, "%i is out of bounds, only %i targets are configured", num, count); + + return ERROR_OK; + } + + while (target) + { + command_print(cmd_ctx, "%i: %s (%s), state: %s", count++, target->type->name, target_endianess_strings[target->endianness], target_state_strings[target->state]); + target = target->next; + } + + return ERROR_OK; +} + +int handle_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int i; + int found = 0; + + if (argc < 3) + { + ERROR("target command requires at least three arguments: "); + exit(-1); + } + + /* search for the specified target */ + if (args[0] && (args[0][0] != 0)) + { + for (i = 0; target_types[i]; i++) + { + if (strcmp(args[0], target_types[i]->name) == 0) + { + target_t **last_target_p = &targets; + + /* register target specific commands */ + if (target_types[i]->register_commands(cmd_ctx) != ERROR_OK) + { + ERROR("couldn't register '%s' commands", args[0]); + exit(-1); + } + + if (*last_target_p) + { + while ((*last_target_p)->next) + last_target_p = &((*last_target_p)->next); + last_target_p = &((*last_target_p)->next); + } + + *last_target_p = malloc(sizeof(target_t)); + + (*last_target_p)->type = target_types[i]; + + if (strcmp(args[1], "big") == 0) + (*last_target_p)->endianness = TARGET_BIG_ENDIAN; + else if (strcmp(args[1], "little") == 0) + (*last_target_p)->endianness = TARGET_LITTLE_ENDIAN; + else + { + ERROR("endianness must be either 'little' or 'big', not '%s'", args[1]); + exit(-1); + } + + /* what to do on a target reset */ + if (strcmp(args[2], "reset_halt") == 0) + (*last_target_p)->reset_mode = RESET_HALT; + else if (strcmp(args[2], "reset_run") == 0) + (*last_target_p)->reset_mode = RESET_RUN; + else if (strcmp(args[2], "reset_init") == 0) + (*last_target_p)->reset_mode = RESET_INIT; + else if (strcmp(args[2], "run_and_halt") == 0) + (*last_target_p)->reset_mode = RESET_RUN_AND_HALT; + else if (strcmp(args[2], "run_and_init") == 0) + (*last_target_p)->reset_mode = RESET_RUN_AND_INIT; + else + { + ERROR("unknown target startup mode %s", args[2]); + exit(-1); + } + (*last_target_p)->run_and_halt_time = 1000; /* default 1s */ + + (*last_target_p)->reset_script = NULL; + (*last_target_p)->post_halt_script = NULL; + (*last_target_p)->pre_resume_script = NULL; + + (*last_target_p)->working_area = 0x0; + (*last_target_p)->working_area_size = 0x0; + (*last_target_p)->working_areas = NULL; + (*last_target_p)->backup_working_area = 0; + + (*last_target_p)->endianness = TARGET_LITTLE_ENDIAN; + (*last_target_p)->state = TARGET_UNKNOWN; + (*last_target_p)->reg_cache = NULL; + (*last_target_p)->breakpoints = NULL; + (*last_target_p)->watchpoints = NULL; + (*last_target_p)->next = NULL; + (*last_target_p)->arch_info = NULL; + + (*last_target_p)->type->target_command(cmd_ctx, cmd, args, argc, *last_target_p); + + found = 1; + break; + } + } + } + + /* no matching target found */ + if (!found) + { + ERROR("target '%s' not found", args[0]); + exit(-1); + } + + return ERROR_OK; +} + +/* usage: target_script */ +int handle_target_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + + if (argc < 3) + { + ERROR("incomplete target_script command"); + exit(-1); + } + + target = get_target_by_num(strtoul(args[0], NULL, 0)); + + if (!target) + { + ERROR("target number '%s' not defined", args[0]); + exit(-1); + } + + if (strcmp(args[1], "reset") == 0) + { + if (target->reset_script) + free(target->reset_script); + target->reset_script = strdup(args[2]); + } + else if (strcmp(args[1], "post_halt") == 0) + { + if (target->post_halt_script) + free(target->post_halt_script); + target->post_halt_script = strdup(args[2]); + } + else if (strcmp(args[1], "pre_resume") == 0) + { + if (target->pre_resume_script) + free(target->pre_resume_script); + target->pre_resume_script = strdup(args[2]); + } + else + { + ERROR("unknown event type: '%s", args[1]); + exit(-1); + } + + return ERROR_OK; +} + +int handle_run_and_halt_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + + if (argc < 2) + { + ERROR("incomplete run_and_halt_time command"); + exit(-1); + } + + target = get_target_by_num(strtoul(args[0], NULL, 0)); + + if (!target) + { + ERROR("target number '%s' not defined", args[0]); + exit(-1); + } + + target->run_and_halt_time = strtoul(args[1], NULL, 0); + + return ERROR_OK; +} + +int handle_working_area_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + + if (argc < 4) + { + ERROR("incomplete working_area command. usage: working_area
<'backup'|'nobackup'>"); + exit(-1); + } + + target = get_target_by_num(strtoul(args[0], NULL, 0)); + + if (!target) + { + ERROR("target number '%s' not defined", args[0]); + exit(-1); + } + + target->working_area = strtoul(args[1], NULL, 0); + target->working_area_size = strtoul(args[2], NULL, 0); + + if (strcmp(args[3], "backup") == 0) + { + target->backup_working_area = 1; + } + else if (strcmp(args[3], "nobackup") == 0) + { + target->backup_working_area = 0; + } + else + { + ERROR("unrecognized argument (%s)", args[3]); + exit(-1); + } + + return ERROR_OK; +} + + +/* process target state changes */ +int handle_target(void *priv) +{ + int retval; + target_t *target = targets; + + while (target) + { + /* only poll if target isn't already halted */ + if (target->state != TARGET_HALTED) + { + if (target_continous_poll) + if ((retval = target->type->poll(target)) < 0) + { + ERROR("couldn't poll target, exiting"); + exit(-1); + } + } + + target = target->next; + } + + return ERROR_OK; +} + +int handle_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target; + reg_t *reg = NULL; + int count = 0; + char *value; + + DEBUG(""); + + target = get_current_target(cmd_ctx); + + /* list all available registers for the current target */ + if (argc == 0) + { + reg_cache_t *cache = target->reg_cache; + + count = 0; + while(cache) + { + int i; + for (i = 0; i < cache->num_regs; i++) + { + value = buf_to_char(cache->reg_list[i].value, cache->reg_list[i].size); + command_print(cmd_ctx, "(%i) %s (/%i): 0x%s (dirty: %i, valid: %i)", count++, cache->reg_list[i].name, cache->reg_list[i].size, value, cache->reg_list[i].dirty, cache->reg_list[i].valid); + free(value); + } + cache = cache->next; + } + + return ERROR_OK; + } + + /* access a single register by its ordinal number */ + if ((args[0][0] >= '0') && (args[0][0] <= '9')) + { + int num = strtoul(args[0], NULL, 0); + reg_cache_t *cache = target->reg_cache; + + count = 0; + while(cache) + { + int i; + for (i = 0; i < cache->num_regs; i++) + { + if (count++ == num) + { + reg = &cache->reg_list[i]; + break; + } + } + if (reg) + break; + cache = cache->next; + } + + if (!reg) + { + command_print(cmd_ctx, "%i is out of bounds, the current target has only %i registers (0 - %i)", num, count, count - 1); + return ERROR_OK; + } + } else /* access a single register by its name */ + { + reg = register_get_by_name(target->reg_cache, args[0], 1); + + if (!reg) + { + command_print(cmd_ctx, "register %s not found in current target", args[0]); + return ERROR_OK; + } + } + + /* display a register */ + if ((argc == 1) || ((argc == 2) && !((args[1][0] >= '0') && (args[1][0] <= '9')))) + { + if ((argc == 2) && (strcmp(args[1], "force") == 0)) + reg->valid = 0; + + if (reg->valid == 0) + { + reg_arch_type_t *arch_type = register_get_arch_type(reg->arch_type); + if (arch_type == NULL) + { + ERROR("BUG: encountered unregistered arch type"); + return ERROR_OK; + } + arch_type->get(reg); + } + value = buf_to_char(reg->value, reg->size); + command_print(cmd_ctx, "%s (/%i): 0x%s", reg->name, reg->size, value); + free(value); + return ERROR_OK; + } + + /* set register value */ + if (argc == 2) + { + u32 new_value = strtoul(args[1], NULL, 0); + reg_arch_type_t *arch_type = register_get_arch_type(reg->arch_type); + if (arch_type == NULL) + { + ERROR("BUG: encountered unregistered arch type"); + return ERROR_OK; + } + + arch_type->set(reg, new_value); + value = buf_to_char(reg->value, reg->size); + command_print(cmd_ctx, "%s (/%i): 0x%s", reg->name, reg->size, value); + free(value); + + return ERROR_OK; + } + + command_print(cmd_ctx, "usage: reg <#|name> [value]"); + + return ERROR_OK; +} + +int handle_poll_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + char buffer[512]; + + if (argc == 0) + { + command_print(cmd_ctx, "target state: %s", target_state_strings[target->type->poll(target)]); + if (target->state == TARGET_HALTED) + { + target->type->arch_state(target, buffer, 512); + buffer[511] = 0; + command_print(cmd_ctx, "%s", buffer); + } + } + else + { + if (strcmp(args[0], "on") == 0) + { + target_continous_poll = 1; + } + else if (strcmp(args[0], "off") == 0) + { + target_continous_poll = 0; + } + } + + + return ERROR_OK; +} + +int handle_wait_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + struct timeval timeout, now; + + gettimeofday(&timeout, NULL); + timeval_add_time(&timeout, 5, 0); + + command_print(cmd_ctx, "waiting for target halted..."); + + while(target->type->poll(target)) + { + if (target->state == TARGET_HALTED) + { + command_print(cmd_ctx, "target halted"); + break; + } + target_call_timer_callbacks(); + + gettimeofday(&now, NULL); + if ((now.tv_sec >= timeout.tv_sec) && (now.tv_usec >= timeout.tv_usec)) + { + command_print(cmd_ctx, "timed out while waiting for target halt"); + ERROR("timed out while waiting for target halt"); + break; + } + } + + return ERROR_OK; +} + +int handle_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + + DEBUG(""); + + command_print(cmd_ctx, "requesting target halt..."); + + if ((retval = target->type->halt(target)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_ALREADY_HALTED: + command_print(cmd_ctx, "target already halted"); + break; + case ERROR_TARGET_TIMEOUT: + command_print(cmd_ctx, "target timed out... shutting down"); + exit(-1); + default: + command_print(cmd_ctx, "unknown error... shutting down"); + exit(-1); + } + } + + return ERROR_OK; + +} + +/* what to do on daemon startup */ +int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 1) + { + if (strcmp(args[0], "attach") == 0) + { + startup_mode = DAEMON_ATTACH; + return ERROR_OK; + } + else if (strcmp(args[0], "reset") == 0) + { + startup_mode = DAEMON_RESET; + return ERROR_OK; + } + } + + WARNING("invalid daemon_startup configuration directive: %s", args[0]); + return ERROR_OK; + +} + +int handle_soft_reset_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + int retval; + + command_print(cmd_ctx, "requesting target halt and executing a soft reset"); + + if ((retval = target->type->soft_reset_halt(target)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_TIMEOUT: + command_print(cmd_ctx, "target timed out... shutting down"); + exit(-1); + default: + command_print(cmd_ctx, "unknown error... shutting down"); + exit(-1); + } + } + + return ERROR_OK; +} + +int handle_reset_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + enum target_reset_mode reset_mode = RESET_RUN; + + DEBUG(""); + + if (argc >= 1) + { + if (strcmp("run", args[0]) == 0) + reset_mode = RESET_RUN; + else if (strcmp("halt", args[0]) == 0) + reset_mode = RESET_HALT; + else if (strcmp("init", args[0]) == 0) + reset_mode = RESET_INIT; + else if (strcmp("run_and_halt", args[0]) == 0) + { + reset_mode = RESET_RUN_AND_HALT; + if (argc >= 2) + { + target->run_and_halt_time = strtoul(args[1], NULL, 0); + } + } + else if (strcmp("run_and_init", args[0]) == 0) + { + reset_mode = RESET_RUN_AND_INIT; + if (argc >= 2) + { + target->run_and_halt_time = strtoul(args[1], NULL, 0); + } + } + else + { + command_print(cmd_ctx, "usage: reset ['run', 'halt', 'init', 'run_and_halt', 'run_and_init]"); + return ERROR_OK; + } + target->reset_mode = reset_mode; + } + + target_process_reset(cmd_ctx); + + return ERROR_OK; +} + +int handle_resume_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + + DEBUG(""); + + if (argc == 0) + retval = target->type->resume(target, 1, 0, 1, 0); /* current pc, addr = 0, handle breakpoints, not debugging */ + else if (argc == 1) + retval = target->type->resume(target, 0, strtoul(args[0], NULL, 0), 1, 0); /* addr = args[0], handle breakpoints, not debugging */ + else + { + command_print(cmd_ctx, "usage: resume [address]"); + return ERROR_OK; + } + + if (retval != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "target not halted"); + break; + default: + command_print(cmd_ctx, "unknown error... shutting down"); + exit(-1); + } + } + + return ERROR_OK; +} + +int handle_step_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + DEBUG(""); + + if (argc == 0) + target->type->step(target, 1, 0, 1); /* current pc, addr = 0, handle breakpoints */ + + if (argc == 1) + target->type->step(target, 0, strtoul(args[0], NULL, 0), 1); /* addr = args[0], handle breakpoints */ + + return ERROR_OK; +} + +int handle_md_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int count = 1; + int size = 4; + u32 address = 0; + int i; + + char output[128]; + int output_len; + + int retval; + + u8 *buffer; + target_t *target = get_current_target(cmd_ctx); + + if (argc < 1) + return ERROR_OK; + + if (argc == 2) + count = strtoul(args[1], NULL, 0); + + address = strtoul(args[0], NULL, 0); + + + switch (cmd[2]) + { + case 'w': + size = 4; + break; + case 'h': + size = 2; + break; + case 'b': + size = 1; + break; + default: + return ERROR_OK; + } + + buffer = calloc(count, size); + if ((retval = target->type->read_memory(target, address, size, count, buffer)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_UNALIGNED_ACCESS: + command_print(cmd_ctx, "error: address not aligned"); + break; + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "error: target must be halted for memory accesses"); + break; + case ERROR_TARGET_DATA_ABORT: + command_print(cmd_ctx, "error: access caused data abort, system possibly corrupted"); + break; + default: + command_print(cmd_ctx, "error: unknown error"); + break; + } + } + + output_len = 0; + + for (i = 0; i < count; i++) + { + if (i%8 == 0) + output_len += snprintf(output + output_len, 128 - output_len, "0x%8.8x: ", address + (i*size)); + + switch (size) + { + case 4: + output_len += snprintf(output + output_len, 128 - output_len, "%8.8x ", ((u32*)buffer)[i]); + break; + case 2: + output_len += snprintf(output + output_len, 128 - output_len, "%4.4x ", ((u16*)buffer)[i]); + break; + case 1: + output_len += snprintf(output + output_len, 128 - output_len, "%2.2x ", ((u8*)buffer)[i]); + break; + } + + if ((i%8 == 7) || (i == count - 1)) + { + command_print(cmd_ctx, output); + output_len = 0; + } + } + + free(buffer); + + return ERROR_OK; +} + +int handle_mw_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 address = 0; + u32 value = 0; + int retval; + target_t *target = get_current_target(cmd_ctx); + + if (argc < 2) + return ERROR_OK; + + address = strtoul(args[0], NULL, 0); + value = strtoul(args[1], NULL, 0); + + switch (cmd[2]) + { + case 'w': + retval = target->type->write_memory(target, address, 4, 1, (u8*)&value); + break; + case 'h': + retval = target->type->write_memory(target, address, 2, 1, (u8*)&value); + break; + case 'b': + retval = target->type->write_memory(target, address, 1, 1, (u8*)&value); + break; + default: + return ERROR_OK; + } + + switch (retval) + { + case ERROR_TARGET_UNALIGNED_ACCESS: + command_print(cmd_ctx, "error: address not aligned"); + break; + case ERROR_TARGET_DATA_ABORT: + command_print(cmd_ctx, "error: access caused data abort, system possibly corrupted"); + break; + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "error: target must be halted for memory accesses"); + break; + case ERROR_OK: + break; + default: + command_print(cmd_ctx, "error: unknown error"); + break; + } + + return ERROR_OK; + +} + +int handle_load_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + FILE *binary; + u32 address; + struct stat binary_stat; + u32 binary_size; + + u8 *buffer; + u32 buf_cnt; + + struct timeval start, end; + + target_t *target = get_current_target(cmd_ctx); + + if (argc != 2) + { + command_print(cmd_ctx, "usage: load_binary
"); + return ERROR_OK; + } + + address = strtoul(args[1], NULL, 0); + + if (stat(args[0], &binary_stat) == -1) + { + ERROR("couldn't stat() %s: %s", args[0], strerror(errno)); + command_print(cmd_ctx, "error accessing file %s", args[0]); + return ERROR_OK; + } + + if (!(binary = fopen(args[0], "r"))) + { + ERROR("couldn't open %s: %s", args[0], strerror(errno)); + command_print(cmd_ctx, "error accessing file %s", args[0]); + return ERROR_OK; + } + + buffer = malloc(128 * 1024); + + gettimeofday(&start, NULL); + + binary_size = binary_stat.st_size; + while (binary_size > 0) + { + buf_cnt = fread(buffer, 1, 128*1024, binary); + target_write_buffer(target, address, buf_cnt, buffer); + address += buf_cnt; + binary_size -= buf_cnt; + } + + gettimeofday(&end, NULL); + + free(buffer); + + command_print(cmd_ctx, "downloaded %lli byte in %is %ius", (long long) binary_stat.st_size, end.tv_sec - start.tv_sec, end.tv_usec - start.tv_usec); + + fclose(binary); + + return ERROR_OK; + +} + +int handle_dump_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + FILE *binary; + u32 address; + u32 size; + u8 buffer[560]; + + target_t *target = get_current_target(cmd_ctx); + + if (argc != 3) + { + command_print(cmd_ctx, "usage: dump_binary
"); + return ERROR_OK; + } + + address = strtoul(args[1], NULL, 0); + size = strtoul(args[2], NULL, 0); + + if (!(binary = fopen(args[0], "w"))) + { + ERROR("couldn't open %s for writing: %s", args[0], strerror(errno)); + command_print(cmd_ctx, "error accessing file %s", args[0]); + return ERROR_OK; + } + + if ((address & 3) || (size & 3)) + { + command_print(cmd_ctx, "only 32-bit aligned address and size are supported"); + return ERROR_OK; + } + + while (size > 0) + { + u32 this_run_size = (size > 560) ? 560 : size; + target->type->read_memory(target, address, 4, this_run_size / 4, buffer); + fwrite(buffer, 1, this_run_size, binary); + size -= this_run_size; + address += this_run_size; + } + + fclose(binary); + + return ERROR_OK; + +} + +int handle_bp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + + if (argc == 0) + { + breakpoint_t *breakpoint = target->breakpoints; + + while (breakpoint) + { + if (breakpoint->type == BKPT_SOFT) + { + char* buf = buf_to_char(breakpoint->orig_instr, breakpoint->length); + command_print(cmd_ctx, "0x%8.8x, 0x%x, %i, 0x%s", breakpoint->address, breakpoint->length, breakpoint->set, buf); + free(buf); + } + else + { + command_print(cmd_ctx, "0x%8.8x, 0x%x, %i", breakpoint->address, breakpoint->length, breakpoint->set); + } + breakpoint = breakpoint->next; + } + } + else if (argc >= 2) + { + int hw = BKPT_SOFT; + u32 length = 0; + + length = strtoul(args[1], NULL, 0); + + if (argc >= 3) + if (strcmp(args[2], "hw") == 0) + hw = BKPT_HARD; + + if ((retval = breakpoint_add(target, strtoul(args[0], NULL, 0), length, hw)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "target must be halted to set breakpoints"); + break; + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: + command_print(cmd_ctx, "no more breakpoints available"); + break; + default: + command_print(cmd_ctx, "unknown error, breakpoint not set"); + break; + } + } + } + + return ERROR_OK; +} + +int handle_rbp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + if (argc > 0) + breakpoint_remove(target, strtoul(args[0], NULL, 0)); + + return ERROR_OK; +} + +int handle_wp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + if (argc == 0) + { + watchpoint_t *watchpoint = target->watchpoints; + + while (watchpoint) + { + command_print(cmd_ctx, "address: 0x%8.8x, mask: 0x%8.8x, r/w/a: %i, value: 0x%8.8x, mask: 0x%8.8x", watchpoint->address, watchpoint->length, watchpoint->rw, watchpoint->value, watchpoint->mask); + watchpoint = watchpoint->next; + } + } + else if (argc >= 2) + { + enum watchpoint_rw type = WPT_ACCESS; + u32 data_value = 0x0; + u32 data_mask = 0xffffffff; + + if (argc >= 3) + { + switch(args[2][0]) + { + case 'r': + type = WPT_READ; + break; + case 'w': + type = WPT_WRITE; + break; + case 'a': + type = WPT_ACCESS; + break; + default: + command_print(cmd_ctx, "usage: wp
[r/w/a] [value] [mask]"); + return ERROR_OK; + } + } + if (argc >= 4) + { + data_value = strtoul(args[3], NULL, 0); + } + if (argc >= 5) + { + data_mask = strtoul(args[4], NULL, 0); + } + watchpoint_add(target, strtoul(args[0], NULL, 0), strtoul(args[1], NULL, 0), type, data_value, data_mask); + } + else + { + command_print(cmd_ctx, "usage: wp
[r/w/a] [value] [mask]"); + } + + return ERROR_OK; +} + +int handle_rwp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + if (argc > 0) + watchpoint_remove(target, strtoul(args[0], NULL, 0)); + + return ERROR_OK; +} + -- cgit v1.2.3