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/Makefile.am | 7 + src/target/algorithm.c | 54 + src/target/algorithm.h | 53 + src/target/arm720t.c | 625 ++++++++++++ src/target/arm720t.h | 43 + src/target/arm7_9_common.c | 2339 ++++++++++++++++++++++++++++++++++++++++++++ src/target/arm7_9_common.h | 129 +++ src/target/arm7tdmi.c | 780 +++++++++++++++ src/target/arm7tdmi.h | 46 + src/target/arm920t.c | 967 ++++++++++++++++++ src/target/arm920t.h | 45 + src/target/arm9tdmi.c | 848 ++++++++++++++++ src/target/arm9tdmi.h | 51 + src/target/arm_jtag.c | 116 +++ src/target/arm_jtag.h | 42 + src/target/armv4_5.c | 583 +++++++++++ src/target/armv4_5.h | 237 +++++ src/target/armv4_5_cache.c | 112 +++ src/target/armv4_5_cache.h | 49 + src/target/armv4_5_mmu.c | 358 +++++++ src/target/armv4_5_mmu.h | 52 + src/target/breakpoints.c | 219 +++++ src/target/breakpoints.h | 70 ++ src/target/embeddedice.c | 301 ++++++ src/target/embeddedice.h | 90 ++ src/target/etm.c | 409 ++++++++ src/target/etm.h | 76 ++ src/target/register.c | 100 ++ src/target/register.h | 69 ++ src/target/target.c | 1701 ++++++++++++++++++++++++++++++++ src/target/target.h | 231 +++++ 31 files changed, 10802 insertions(+) create mode 100644 src/target/Makefile.am create mode 100644 src/target/algorithm.c create mode 100644 src/target/algorithm.h create mode 100644 src/target/arm720t.c create mode 100644 src/target/arm720t.h create mode 100644 src/target/arm7_9_common.c create mode 100644 src/target/arm7_9_common.h create mode 100644 src/target/arm7tdmi.c create mode 100644 src/target/arm7tdmi.h create mode 100644 src/target/arm920t.c create mode 100644 src/target/arm920t.h create mode 100644 src/target/arm9tdmi.c create mode 100644 src/target/arm9tdmi.h create mode 100644 src/target/arm_jtag.c create mode 100644 src/target/arm_jtag.h create mode 100644 src/target/armv4_5.c create mode 100644 src/target/armv4_5.h create mode 100644 src/target/armv4_5_cache.c create mode 100644 src/target/armv4_5_cache.h create mode 100644 src/target/armv4_5_mmu.c create mode 100644 src/target/armv4_5_mmu.h create mode 100644 src/target/breakpoints.c create mode 100644 src/target/breakpoints.h create mode 100644 src/target/embeddedice.c create mode 100644 src/target/embeddedice.h create mode 100644 src/target/etm.c create mode 100644 src/target/etm.h create mode 100644 src/target/register.c create mode 100644 src/target/register.h create mode 100644 src/target/target.c create mode 100644 src/target/target.h (limited to 'src/target') diff --git a/src/target/Makefile.am b/src/target/Makefile.am new file mode 100644 index 00000000..7e26762b --- /dev/null +++ b/src/target/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I$(top_srcdir)/src/gdb -I$(top_srcdir)/src/helper -I$(top_srcdir)/src/jtag -I$(top_srcdir)/src/xsvf $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libtarget.a +libtarget_a_SOURCES = target.c register.c breakpoints.c armv4_5.c embeddedice.c etm.c arm7tdmi.c arm9tdmi.c \ + arm_jtag.c arm7_9_common.c algorithm.c arm920t.c arm720t.c armv4_5_mmu.c armv4_5_cache.c +noinst_HEADERS = target.h register.h armv4_5.h embeddedice.h etm.h arm7tdmi.h arm9tdmi.h \ + arm_jtag.h arm7_9_common.h arm920t.h arm720t.h armv4_5_mmu.h armv4_5_cache.h breakpoints.h algorithm.h diff --git a/src/target/algorithm.c b/src/target/algorithm.c new file mode 100644 index 00000000..fdebfc58 --- /dev/null +++ b/src/target/algorithm.c @@ -0,0 +1,54 @@ +/*************************************************************************** + * 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 "algorithm.h" + +#include "log.h" +#include "configuration.h" +#include "binarybuffer.h" + +#include + + +void init_mem_param(mem_param_t *param, u32 address, u32 size, enum param_direction direction) +{ + param->address = address; + param->size = size; + param->value = malloc(size); + param->direction = direction; +} + +void destroy_mem_param(mem_param_t *param) +{ + free(param->value); +} + +void init_reg_param(reg_param_t *param, char *reg_name, u32 size, enum param_direction direction) +{ + param->reg_name = reg_name; + param->size = size; + param->value = malloc(CEIL(size, 8)); + param->direction = direction; +} + +void destroy_reg_param(reg_param_t *param) +{ + free(param->value); +} diff --git a/src/target/algorithm.h b/src/target/algorithm.h new file mode 100644 index 00000000..e248ba5a --- /dev/null +++ b/src/target/algorithm.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ALGORITHM_H +#define ALGORITHM_H + +#include "types.h" + +enum param_direction +{ + PARAM_IN, + PARAM_OUT, + PARAM_IN_OUT +}; + +typedef struct mem_param_s +{ + u32 address; + u32 size; + u8 *value; + enum param_direction direction; +} mem_param_t; + +typedef struct reg_param_s +{ + char *reg_name; + u32 size; + u8 *value; + enum param_direction direction; +} reg_param_t; + +extern void init_mem_param(mem_param_t *param, u32 address, u32 size, enum param_direction direction); +extern void destroy_mem_param(mem_param_t *param); +extern void init_reg_param(reg_param_t *param, char *reg_name, u32 size, enum param_direction direction); +extern void destroy_reg_param(reg_param_t *param); + +#endif /* ALGORITHM_H */ diff --git a/src/target/arm720t.c b/src/target/arm720t.c new file mode 100644 index 00000000..68ea3d1c --- /dev/null +++ b/src/target/arm720t.c @@ -0,0 +1,625 @@ +/*************************************************************************** + * 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 "arm720t.h" +#include "jtag.h" +#include "log.h" + +#include +#include + +#if 1 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm720t_register_commands(struct command_context_s *cmd_ctx); + +int arm720t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm720t_handle_virt2phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm720t_handle_md_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm720t_handle_mw_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* forward declarations */ +int arm720t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm720t_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm720t_quit(); +int arm720t_arch_state(struct target_s *target, char *buf, int buf_size); +int arm720t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm720t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm720t_soft_reset_halt(struct target_s *target); + +target_type_t arm720t_target = +{ + .name = "arm720t", + + .poll = arm7_9_poll, + .arch_state = arm720t_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm720t_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm720t_read_memory, + .write_memory = arm720t_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm720t_register_commands, + .target_command = arm720t_target_command, + .init_target = arm720t_init_target, + .quit = arm720t_quit +}; + +int arm720t_scan_cp15(target_t *target, u32 out, u32 *in, int instruction, int clock) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + scan_field_t fields[2]; + u8 out_buf[4]; + u8 instruction_buf = instruction; + + out = flip_u32(out, 32); + buf_set_u32(out_buf, 0, 32, out); + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &instruction_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = out_buf; + fields[1].out_mask = NULL; + if (in) + { + fields[1].in_value = (u8*)in; + fields[1].in_handler = arm_jtag_buf_to_u32_flip; + fields[1].in_handler_priv = in; + } else + { + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + } + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + jtag_add_dr_scan(2, fields, -1); + + if (clock) + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ + jtag_execute_queue(); + + if (in) + DEBUG("out: %8.8x, in: %8.8x, instruction: %i, clock: %i", out, *in, instruction, clock); + else + DEBUG("out: %8.8x, instruction: %i, clock: %i", out, instruction, clock); +#else + DEBUG("out: %8.8x, instruction: %i, clock: %i", in, out, instruction, clock); +#endif + + return ERROR_OK; +} + +int arm720t_read_cp15(target_t *target, u32 opcode, u32 *value) +{ + /* fetch CP15 opcode */ + arm720t_scan_cp15(target, opcode, NULL, 1, 1); + /* "DECODE" stage */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 1); + /* "EXECUTE" stage (1) */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 0); + arm720t_scan_cp15(target, 0x0, NULL, 0, 1); + /* "EXECUTE" stage (2) */ + arm720t_scan_cp15(target, 0x0, NULL, 0, 1); + /* "EXECUTE" stage (3), CDATA is read */ + arm720t_scan_cp15(target, ARMV4_5_NOP, value, 1, 1); + + return ERROR_OK; +} + +int arm720t_write_cp15(target_t *target, u32 opcode, u32 value) +{ + /* fetch CP15 opcode */ + arm720t_scan_cp15(target, opcode, NULL, 1, 1); + /* "DECODE" stage */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 1); + /* "EXECUTE" stage (1) */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 0); + arm720t_scan_cp15(target, 0x0, NULL, 0, 1); + /* "EXECUTE" stage (2) */ + arm720t_scan_cp15(target, value, NULL, 0, 1); + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 1); + + return ERROR_OK; +} + +u32 arm720t_get_ttb(target_t *target) +{ + u32 ttb = 0x0; + + arm720t_read_cp15(target, 0xee120f10, &ttb); + jtag_execute_queue(); + + ttb &= 0xffffc000; + + return ttb; +} + +void arm720t_disable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm720t_read_cp15(target, 0xee110f10, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control &= ~0x1U; + + if (d_u_cache || i_cache) + cp15_control &= ~0x4U; + + arm720t_write_cp15(target, 0xee010f10, cp15_control); +} + +void arm720t_enable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm720t_read_cp15(target, 0xee110f10, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control |= 0x1U; + + if (d_u_cache || i_cache) + cp15_control |= 0x4U; + + arm720t_write_cp15(target, 0xee010f10, cp15_control); +} + +void arm720t_post_debug_entry(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + /* examine cp15 control reg */ + arm720t_read_cp15(target, 0xee110f10, &arm720t->cp15_control_reg); + jtag_execute_queue(); + DEBUG("cp15_control_reg: %8.8x", arm720t->cp15_control_reg); + + arm720t->armv4_5_mmu.mmu_enabled = (arm720t->cp15_control_reg & 0x1U) ? 1 : 0; + arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = (arm720t->cp15_control_reg & 0x4U) ? 1 : 0; + arm720t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 0; + + /* save i/d fault status and address register */ + arm720t_read_cp15(target, 0xee150f10, &arm720t->fsr); + arm720t_read_cp15(target, 0xee160f10, &arm720t->far); + jtag_execute_queue(); +} + +void arm720t_pre_restore_context(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + /* restore i/d fault status and address register */ + arm720t_write_cp15(target, 0xee050f10, arm720t->fsr); + arm720t_write_cp15(target, 0xee060f10, arm720t->far); +} + +int arm720t_get_arch_pointers(target_t *target, armv4_5_common_t **armv4_5_p, arm7_9_common_t **arm7_9_p, arm7tdmi_common_t **arm7tdmi_p, arm720t_common_t **arm720t_p) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + return -1; + } + + arm7_9 = armv4_5->arch_info; + if (arm7_9->common_magic != ARM7_9_COMMON_MAGIC) + { + return -1; + } + + arm7tdmi = arm7_9->arch_info; + if (arm7tdmi->common_magic != ARM7TDMI_COMMON_MAGIC) + { + return -1; + } + + arm720t = arm7tdmi->arch_info; + if (arm720t->common_magic != ARM720T_COMMON_MAGIC) + { + return -1; + } + + *armv4_5_p = armv4_5; + *arm7_9_p = arm7_9; + *arm7tdmi_p = arm7tdmi; + *arm720t_p = arm720t; + + return ERROR_OK; +} + +int arm720t_arch_state(struct target_s *target, char *buf, int buf_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + char *state[] = + { + "disabled", "enabled" + }; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("BUG: called for a non-ARMv4/5 target"); + exit(-1); + } + + snprintf(buf, buf_size, + "target halted in %s state due to %s, current mode: %s\n" + "cpsr: 0x%8.8x pc: 0x%8.8x\n" + "MMU: %s, Cache: %s", + armv4_5_state_strings[armv4_5->core_state], + target_debug_reason_strings[target->debug_reason], + armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)], + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32), + state[arm720t->armv4_5_mmu.mmu_enabled], + state[arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled]); + + return ERROR_OK; +} + +int arm720t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + /* disable cache, but leave MMU enabled */ + if (arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled) + arm720t_disable_mmu_caches(target, 0, 1, 0); + + retval = arm7_9_read_memory(target, address, size, count, buffer); + + if (arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled) + arm720t_enable_mmu_caches(target, 0, 1, 0); + + return retval; +} + +int arm720t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + if ((retval = arm7_9_write_memory(target, address, size, count, buffer)) != ERROR_OK) + return retval; + + return retval; +} + +int arm720t_soft_reset_halt(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + if (target->state == TARGET_RUNNING) + { + target->type->halt(target); + } + + while (buf_get_u32(dbg_stat->value, EICE_DBG_CONTROL_DBGACK, 1) == 0) + { + embeddedice_read_reg(dbg_stat); + jtag_execute_queue(); + } + + target->state = TARGET_HALTED; + + /* SVC, ARM state, IRQ and FIQ disabled */ + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8, 0xd3); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + /* start fetching from 0x0 */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, 0x0); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + + armv4_5->core_mode = ARMV4_5_MODE_SVC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + arm720t_disable_mmu_caches(target, 1, 1, 1); + arm720t->armv4_5_mmu.mmu_enabled = 0; + arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = 0; + arm720t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 0; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; +} + +int arm720t_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + arm7tdmi_init_target(cmd_ctx, target); + + return ERROR_OK; + +} + +int arm720t_quit() +{ + + return ERROR_OK; +} + +int arm720t_init_arch_info(target_t *target, arm720t_common_t *arm720t, int chain_pos, char *variant) +{ + arm7tdmi_common_t *arm7tdmi = &arm720t->arm7tdmi_common; + arm7_9_common_t *arm7_9 = &arm7tdmi->arm7_9_common; + + arm7tdmi_init_arch_info(target, arm7tdmi, chain_pos, variant); + + arm7tdmi->arch_info = arm720t; + arm720t->common_magic = ARM720T_COMMON_MAGIC; + + arm7_9->post_debug_entry = arm720t_post_debug_entry; + arm7_9->pre_restore_context = arm720t_pre_restore_context; + + arm720t->armv4_5_mmu.armv4_5_cache.ctype = -1; + arm720t->armv4_5_mmu.get_ttb = arm720t_get_ttb; + arm720t->armv4_5_mmu.read_memory = arm7_9_read_memory; + arm720t->armv4_5_mmu.write_memory = arm7_9_write_memory; + arm720t->armv4_5_mmu.disable_mmu_caches = arm720t_disable_mmu_caches; + arm720t->armv4_5_mmu.enable_mmu_caches = arm720t_enable_mmu_caches; + arm720t->armv4_5_mmu.has_tiny_pages = 0; + arm720t->armv4_5_mmu.mmu_enabled = 0; + + return ERROR_OK; +} + +int arm720t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm720t_common_t *arm720t = malloc(sizeof(arm720t_common_t)); + + if (argc < 4) + { + ERROR("'target arm720t' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[3], NULL, 0); + + if (argc >= 5) + variant = strdup(args[4]); + + DEBUG("chain_pos: %i, variant: %s", chain_pos, variant); + + arm720t_init_arch_info(target, arm720t, chain_pos, variant); + + return ERROR_OK; +} + +int arm720t_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + command_t *arm720t_cmd; + + + retval = arm7tdmi_register_commands(cmd_ctx); + + arm720t_cmd = register_command(cmd_ctx, NULL, "arm720t", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, arm720t_cmd, "cp15", arm720t_handle_cp15_command, COMMAND_EXEC, "display/modify cp15 register [value]"); + register_command(cmd_ctx, arm720t_cmd, "virt2phys", arm720t_handle_virt2phys_command, COMMAND_EXEC, "translate va to pa "); + + register_command(cmd_ctx, arm720t_cmd, "mdw_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory words [count]"); + register_command(cmd_ctx, arm720t_cmd, "mdh_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory half-words [count]"); + register_command(cmd_ctx, arm720t_cmd, "mdb_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory bytes [count]"); + + register_command(cmd_ctx, arm720t_cmd, "mww_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory word "); + register_command(cmd_ctx, arm720t_cmd, "mwh_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory half-word "); + register_command(cmd_ctx, arm720t_cmd, "mwb_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory byte "); + + return ERROR_OK; +} + +int arm720t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + /* one or more argument, access a single register (write if second argument is given */ + if (argc >= 1) + { + u32 opcode = strtoul(args[0], NULL, 0); + + if (argc == 1) + { + u32 value; + if ((retval = arm720t_read_cp15(target, opcode, &value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access cp15 with opcode 0x%8.8x", opcode); + return ERROR_OK; + } + jtag_execute_queue(); + + command_print(cmd_ctx, "0x%8.8x: 0x%8.8x", opcode, value); + } + else if (argc == 2) + { + u32 value = strtoul(args[1], NULL, 0); + if ((retval = arm720t_write_cp15(target, opcode, value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access cp15 with opcode 0x%8.8x", opcode); + return ERROR_OK; + } + command_print(cmd_ctx, "0x%8.8x: 0x%8.8x", opcode, value); + } + } + + return ERROR_OK; +} + +int arm720t_handle_virt2phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_virt2phys_command(cmd_ctx, cmd, args, argc, target, &arm720t->armv4_5_mmu); +} + +int arm720t_handle_md_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_md_phys_command(cmd_ctx, cmd, args, argc, target, &arm720t->armv4_5_mmu); +} + +int arm720t_handle_mw_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_mw_phys_command(cmd_ctx, cmd, args, argc, target, &arm720t->armv4_5_mmu); +} + diff --git a/src/target/arm720t.h b/src/target/arm720t.h new file mode 100644 index 00000000..2479b548 --- /dev/null +++ b/src/target/arm720t.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARM720T_H +#define ARM720T_H + +#include "target.h" +#include "register.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm7tdmi.h" +#include "armv4_5_mmu.h" +#include "armv4_5_cache.h" + +#define ARM720T_COMMON_MAGIC 0xa720a720 + +typedef struct arm720t_common_s +{ + int common_magic; + armv4_5_mmu_common_t armv4_5_mmu; + arm7tdmi_common_t arm7tdmi_common; + u32 cp15_control_reg; + u32 fsr; + u32 far; +} arm720t_common_t; + +#endif /* ARM720T_H */ diff --git a/src/target/arm7_9_common.c b/src/target/arm7_9_common.c new file mode 100644 index 00000000..d167041f --- /dev/null +++ b/src/target/arm7_9_common.c @@ -0,0 +1,2339 @@ +/*************************************************************************** + * 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 "embeddedice.h" +#include "target.h" +#include "armv4_5.h" +#include "arm_jtag.h" +#include "jtag.h" +#include "log.h" +#include "arm7_9_common.h" +#include "breakpoints.h" + +#include +#include +#include + +#include +#include +#include +#include + +int arm7_9_debug_entry(target_t *target); +int arm7_9_enable_sw_bkpts(struct target_s *target); + +/* command handler forward declarations */ +int handle_arm7_9_write_xpsr_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_write_xpsr_im8_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_read_core_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_write_core_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_sw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_force_hw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_dbgrq_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_fast_writes_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_dcc_downloads_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int arm7_9_reinit_embeddedice(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + breakpoint_t *breakpoint = target->breakpoints; + + arm7_9->wp_available = 2; + arm7_9->wp0_used = 0; + arm7_9->wp1_used = 0; + + /* mark all hardware breakpoints as unset */ + while (breakpoint) + { + if (breakpoint->type == BKPT_HARD) + { + breakpoint->set = 0; + } + breakpoint = breakpoint->next; + } + + if (arm7_9->sw_bkpts_enabled && arm7_9->sw_bkpts_use_wp) + { + arm7_9->sw_bkpts_enabled = 0; + arm7_9_enable_sw_bkpts(target); + } + + arm7_9->reinit_embeddedice = 0; + + return ERROR_OK; +} + +int arm7_9_jtag_callback(enum jtag_event event, void *priv) +{ + target_t *target = priv; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* a test-logic reset occured + * the EmbeddedICE registers have been reset + * hardware breakpoints have been cleared + */ + if (event == JTAG_TRST_ASSERTED) + { + arm7_9->reinit_embeddedice = 1; + } + + return ERROR_OK; +} + +int arm7_9_get_arch_pointers(target_t *target, armv4_5_common_t **armv4_5_p, arm7_9_common_t **arm7_9_p) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + return -1; + } + + if (arm7_9->common_magic != ARM7_9_COMMON_MAGIC) + { + return -1; + } + + *armv4_5_p = armv4_5; + *arm7_9_p = arm7_9; + + return ERROR_OK; +} + +int arm7_9_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->force_hw_bkpts) + breakpoint->type = BKPT_HARD; + + if (breakpoint->set) + { + WARNING("breakpoint already set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) + { + /* either an ARM (4 byte) or Thumb (2 byte) breakpoint */ + u32 mask = (breakpoint->length == 4) ? 0x3u : 0x1u; + if (!arm7_9->wp0_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_VALUE], breakpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + + jtag_execute_queue(); + arm7_9->wp0_used = 1; + breakpoint->set = 1; + } + else if (!arm7_9->wp1_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], breakpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + + jtag_execute_queue(); + arm7_9->wp1_used = 1; + breakpoint->set = 2; + } + else + { + ERROR("BUG: no hardware comparator available"); + return ERROR_OK; + } + } + else if (breakpoint->type == BKPT_SOFT) + { + if (breakpoint->length == 4) + { + target->type->read_memory(target, breakpoint->address, 4, 1, breakpoint->orig_instr); + target->type->write_memory(target, breakpoint->address, 4, 1, (u8*)(&arm7_9->arm_bkpt)); + } + else + { + target->type->read_memory(target, breakpoint->address, 2, 1, breakpoint->orig_instr); + target->type->read_memory(target, breakpoint->address, 2, 1, (u8*)(&arm7_9->arm_bkpt)); + } + breakpoint->set = 1; + } + + return ERROR_OK; + +} + +int arm7_9_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!breakpoint->set) + { + WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) + { + if (breakpoint->set == 1) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp0_used = 0; + } + else if (breakpoint->set == 2) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp1_used = 0; + } + breakpoint->set = 0; + } + else + { + if (breakpoint->length == 4) + { + target->type->write_memory(target, breakpoint->address, 4, 1, breakpoint->orig_instr); + } + else + { + target->type->write_memory(target, breakpoint->address, 2, 1, breakpoint->orig_instr); + } + breakpoint->set = 0; + } + + return ERROR_OK; +} + +int arm7_9_add_breakpoint(struct target_s *target, u32 address, u32 length, enum breakpoint_type type) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->force_hw_bkpts) + { + type = BKPT_HARD; + } + + if ((type == BKPT_SOFT) && (arm7_9->sw_bkpts_enabled == 0)) + { + INFO("sw breakpoint requested, but software breakpoints not enabled"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if ((type == BKPT_HARD) && (arm7_9->wp_available < 1)) + { + INFO("no watchpoint unit available for hardware breakpoint"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (type == BKPT_HARD) + arm7_9->wp_available--; + + if ((length != 2) && (length != 4)) + { + INFO("only breakpoints of two (Thumb) or four (ARM) bytes length supported"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + return ERROR_OK; +} + +int arm7_9_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (breakpoint->set) + { + arm7_9_unset_breakpoint(target, breakpoint); + } + + arm7_9->wp_available++; + + return ERROR_OK; +} + +int arm7_9_set_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + int rw_mask = 1; + u32 mask; + + mask = watchpoint->length - 1; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (watchpoint->rw == WPT_ACCESS) + rw_mask = 0; + else + rw_mask = 1; + + if (!arm7_9->wp0_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_VALUE], watchpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], watchpoint->mask); + if( watchpoint->mask != 0xffffffffu ) + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_VALUE], watchpoint->value); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], 0xff & ~EICE_W_CTRL_nOPC & ~rw_mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], EICE_W_CTRL_ENABLE | EICE_W_CTRL_nOPC | (watchpoint->rw & 1)); + + jtag_execute_queue(); + watchpoint->set = 1; + arm7_9->wp0_used = 2; + } + else if (!arm7_9->wp1_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], watchpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], watchpoint->mask); + if( watchpoint->mask != 0xffffffffu ) + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_VALUE], watchpoint->value); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], 0xff & ~EICE_W_CTRL_nOPC & ~rw_mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], EICE_W_CTRL_ENABLE | EICE_W_CTRL_nOPC | (watchpoint->rw & 1)); + + jtag_execute_queue(); + watchpoint->set = 2; + arm7_9->wp1_used = 2; + } + else + { + ERROR("BUG: no hardware comparator available"); + return ERROR_OK; + } + + return ERROR_OK; +} + +int arm7_9_unset_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!watchpoint->set) + { + WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (watchpoint->set == 1) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp0_used = 0; + } + else if (watchpoint->set == 2) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp1_used = 0; + } + watchpoint->set = 0; + + return ERROR_OK; +} + +int arm7_9_add_watchpoint(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->wp_available < 1) + { + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if ((length != 1) && (length != 2) && (length != 4)) + { + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + arm7_9->wp_available--; + + return ERROR_OK; +} + +int arm7_9_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (watchpoint->set) + { + arm7_9_unset_watchpoint(target, watchpoint); + } + + arm7_9->wp_available++; + + return ERROR_OK; +} + +int arm7_9_enable_sw_bkpts(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + int retval; + + if (arm7_9->sw_bkpts_enabled) + return ERROR_OK; + + if (arm7_9->wp_available-- < 1) + { + WARNING("can't enable sw breakpoints with no watchpoint unit available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (!arm7_9->wp0_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_VALUE], arm7_9->arm_bkpt); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0x0); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + arm7_9->sw_bkpts_enabled = 1; + arm7_9->wp0_used = 3; + } + else if (!arm7_9->wp1_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_VALUE], arm7_9->arm_bkpt); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], 0x0); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + arm7_9->sw_bkpts_enabled = 2; + arm7_9->wp1_used = 3; + } + else + { + ERROR("BUG: both watchpoints used, but wp_available >= 1"); + exit(-1); + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("error writing EmbeddedICE registers to enable sw breakpoints"); + exit(-1); + }; + + return ERROR_OK; +} + +int arm7_9_disable_sw_bkpts(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (!arm7_9->sw_bkpts_enabled) + return ERROR_OK; + + if (arm7_9->sw_bkpts_enabled == 1) + { + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); + arm7_9->sw_bkpts_enabled = 0; + arm7_9->wp0_used = 0; + arm7_9->wp_available++; + } + else if (arm7_9->sw_bkpts_enabled == 2) + { + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + arm7_9->sw_bkpts_enabled = 0; + arm7_9->wp1_used = 0; + arm7_9->wp_available++; + } + + return ERROR_OK; +} + +int arm7_9_execute_sys_speed(struct target_s *target) +{ + int timeout; + int retval; + + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* set RESTART instruction */ + jtag_add_end_state(TAP_RTI); + arm_jtag_set_instr(jtag_info, 0x4); + + for (timeout=0; timeout<50; timeout++) + { + /* read debug status register */ + embeddedice_read_reg(dbg_stat); + if ((retval = jtag_execute_queue()) != ERROR_OK) + return retval; + if ((buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_DBGACK, 1)) + && (buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_SYSCOMP, 1))) + break; + usleep(100000); + } + if (timeout == 50) + { + ERROR("timeout waiting for SYSCOMP & DBGACK, last DBG_STATUS: %x", buf_get_u32(dbg_stat->value, 0, dbg_stat->size)); + return ERROR_TARGET_TIMEOUT; + } + + return ERROR_OK; +} + +int arm7_9_execute_fast_sys_speed(struct target_s *target) +{ + u8 check_value[4], check_mask[4]; + + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* set RESTART instruction */ + jtag_add_end_state(TAP_RTI); + arm_jtag_set_instr(jtag_info, 0x4); + + /* check for DBGACK and SYSCOMP set (others don't care) */ + buf_set_u32(check_value, 0, 32, 0x9); + buf_set_u32(check_mask, 0, 32, 0x9); + + /* read debug status register */ + embeddedice_read_reg_w_check(dbg_stat, check_value, check_value); + + return ERROR_OK; +} + +enum target_state arm7_9_poll(target_t *target) +{ + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + if (arm7_9->reinit_embeddedice) + { + arm7_9_reinit_embeddedice(target); + } + + /* read debug status register */ + embeddedice_read_reg(dbg_stat); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + switch (retval) + { + case ERROR_JTAG_QUEUE_FAILED: + ERROR("JTAG queue failed while reading EmbeddedICE status register"); + exit(-1); + break; + default: + break; + } + } + + if (buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_DBGACK, 1)) + { + DEBUG("DBGACK set, dbg_state->value: 0x%x", buf_get_u32(dbg_stat->value, 0, 32)); + if ((target->state == TARGET_UNKNOWN)) + { + WARNING("DBGACK set while target was in unknown state. Reset or initialize target before resuming"); + target->state = TARGET_RUNNING; + } + if ((target->state == TARGET_RUNNING) || (target->state == TARGET_RESET)) + { + target->state = TARGET_HALTED; + if ((retval = arm7_9_debug_entry(target)) != ERROR_OK) + return retval; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + if (target->state == TARGET_DEBUG_RUNNING) + { + target->state = TARGET_HALTED; + if ((retval = arm7_9_debug_entry(target)) != ERROR_OK) + return retval; + + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED); + } + } + else + { + if (target->state != TARGET_DEBUG_RUNNING) + target->state = TARGET_RUNNING; + } + + return target->state; +} + +int arm7_9_assert_reset(target_t *target) +{ + int retval; + + DEBUG("target->state: %s", target_state_strings[target->state]); + + if (target->state == TARGET_HALTED || target->state == TARGET_UNKNOWN) + { + /* assert SRST and TRST */ + /* system would get ouf sync if we didn't reset test-logic, too */ + if ((retval = jtag_add_reset(1, 1)) != ERROR_OK) + { + if (retval == ERROR_JTAG_RESET_CANT_SRST) + { + WARNING("can't assert srst"); + return retval; + } + else + { + ERROR("unknown error"); + exit(-1); + } + } + jtag_add_sleep(5000); + if ((retval = jtag_add_reset(0, 1)) != ERROR_OK) + { + if (retval == ERROR_JTAG_RESET_WOULD_ASSERT_TRST) + { + WARNING("srst resets test logic, too"); + retval = jtag_add_reset(1, 1); + } + } + } + else + { + if ((retval = jtag_add_reset(0, 1)) != ERROR_OK) + { + if (retval == ERROR_JTAG_RESET_WOULD_ASSERT_TRST) + { + WARNING("srst resets test logic, too"); + retval = jtag_add_reset(1, 1); + } + + if (retval == ERROR_JTAG_RESET_CANT_SRST) + { + WARNING("can't assert srst"); + return retval; + } + else if (retval != ERROR_OK) + { + ERROR("unknown error"); + exit(-1); + } + } + } + + target->state = TARGET_RESET; + jtag_add_sleep(50000); + + armv4_5_invalidate_core_regs(target); + + return ERROR_OK; + +} + +int arm7_9_deassert_reset(target_t *target) +{ + DEBUG("target->state: %s", target_state_strings[target->state]); + + /* deassert reset lines */ + jtag_add_reset(0, 0); + + return ERROR_OK; + +} + +int arm7_9_soft_reset_halt(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + int i; + + if (target->state == TARGET_RUNNING) + { + target->type->halt(target); + } + + while (buf_get_u32(dbg_stat->value, EICE_DBG_CONTROL_DBGACK, 1) == 0) + { + embeddedice_read_reg(dbg_stat); + jtag_execute_queue(); + } + target->state = TARGET_HALTED; + + /* all register content is now invalid */ + armv4_5_invalidate_core_regs(target); + + /* SVC, ARM state, IRQ and FIQ disabled */ + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8, 0xd3); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + /* start fetching from 0x0 */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, 0x0); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + + armv4_5->core_mode = ARMV4_5_MODE_SVC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + /* reset registers */ + for (i = 0; i <= 14; i++) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).value, 0, 32, 0xffffffff); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).valid = 1; + } + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; +} + +int arm7_9_halt(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + DEBUG("target->state: %s", target_state_strings[target->state]); + + if (target->state == TARGET_HALTED) + { + WARNING("target was already halted"); + return ERROR_TARGET_ALREADY_HALTED; + } + + if (target->state == TARGET_UNKNOWN) + { + WARNING("target was in unknown state when halt was requested"); + } + + if (arm7_9->use_dbgrq) + { + /* program EmbeddedICE Debug Control Register to assert DBGRQ + */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGRQ, 1, 1); + embeddedice_store_reg(dbg_ctrl); + } + else + { + /* program watchpoint unit to match on any address + */ + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x100); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], 0xf7); + } + + target->debug_reason = DBG_REASON_DBGRQ; + + return ERROR_OK; +} + +int arm7_9_clear_halt(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + if (arm7_9->use_dbgrq) + { + /* program EmbeddedICE Debug Control Register to deassert DBGRQ + */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGRQ, 1, 0); + embeddedice_store_reg(dbg_ctrl); + } + else + { + /* restore registers if watchpoint unit 0 was in use + */ + if (arm7_9->wp0_used) + { + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK]); + } + /* control value always has to be restored, as it was either disabled, + * or enabled with possibly different bits + */ + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE]); + } + + return ERROR_OK; +} + +int arm7_9_debug_entry(target_t *target) +{ + int i; + u32 context[16]; + u32* context_p[16]; + u32 r0_thumb, pc_thumb; + u32 cpsr; + int retval; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + +#ifdef _DEBUG_ARM7_9_ + DEBUG(""); +#endif + + if (arm7_9->pre_debug_entry) + arm7_9->pre_debug_entry(target); + + /* program EmbeddedICE Debug Control Register to assert DBGACK and INTDIS + * ensure that DBGRQ is cleared + */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGACK, 1, 1); + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGRQ, 1, 0); + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_INTDIS, 1, 1); + embeddedice_store_reg(dbg_ctrl); + + arm7_9_clear_halt(target); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + switch (retval) + { + case ERROR_JTAG_QUEUE_FAILED: + ERROR("JTAG queue failed while writing EmbeddedICE control register"); + exit(-1); + break; + default: + break; + } + } + + if ((retval = arm7_9->examine_debug_reason(target)) != ERROR_OK) + return retval; + + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* if the target is in Thumb state, change to ARM state */ + if (buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_ITBIT, 1)) + { + DEBUG("target entered debug from Thumb state"); + /* Entered debug from Thumb mode */ + armv4_5->core_state = ARMV4_5_STATE_THUMB; + arm7_9->change_to_arm(target, &r0_thumb, &pc_thumb); + DEBUG("r0_thumb: 0x%8.8x, pc_thumb: 0x%8.8x", r0_thumb, pc_thumb); + } + else + { + DEBUG("target entered debug from ARM state"); + /* Entered debug from ARM mode */ + armv4_5->core_state = ARMV4_5_STATE_ARM; + } + + for (i = 0; i < 16; i++) + context_p[i] = &context[i]; + /* save core registers (r0 - r15 of current core mode) */ + arm7_9->read_core_regs(target, 0xffff, context_p); + + arm7_9->read_xpsr(target, &cpsr, 0); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + return retval; + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, cpsr); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 0; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + armv4_5->core_mode = cpsr & 0x1f; + DEBUG("target entered debug state in %s mode", armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)]); + + if (armv4_5_mode_to_number(armv4_5->core_mode) == -1) + { + target->state = TARGET_UNKNOWN; + ERROR("cpsr contains invalid mode value - communication failure"); + return ERROR_TARGET_FAILURE; + } + + if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + DEBUG("thumb state, applying fixups"); + context[0] = r0_thumb; + context[15] = pc_thumb; + } else if (armv4_5->core_state == ARMV4_5_STATE_ARM) + { + /* adjust value stored by STM */ + context[15] -= 3 * 4; + } + + if ((target->debug_reason == DBG_REASON_BREAKPOINT) + || (target->debug_reason == DBG_REASON_SINGLESTEP) + || (target->debug_reason == DBG_REASON_WATCHPOINT) + || (target->debug_reason == DBG_REASON_WPTANDBKPT) + || ((target->debug_reason == DBG_REASON_DBGRQ) && (arm7_9->use_dbgrq == 0))) + context[15] -= 3 * ((armv4_5->core_state == ARMV4_5_STATE_ARM) ? 4 : 2); + else if (target->debug_reason == DBG_REASON_DBGRQ) + context[15] -= arm7_9->dbgreq_adjust_pc * ((armv4_5->core_state == ARMV4_5_STATE_ARM) ? 4 : 2); + else + { + ERROR("unknown debug reason: %i", target->debug_reason); + } + + + for (i=0; i<=15; i++) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).value, 0, 32, context[i]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).valid = 1; + } + + DEBUG("entered debug state at PC 0x%x", context[15]); + + /* exceptions other than USR & SYS have a saved program status register */ + if ((armv4_5_mode_to_number(armv4_5->core_mode) != ARMV4_5_MODE_USR) && (armv4_5_mode_to_number(armv4_5->core_mode) != ARMV4_5_MODE_SYS)) + { + u32 spsr; + arm7_9->read_xpsr(target, &spsr, 1); + jtag_execute_queue(); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).value, 0, 32, spsr); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).valid = 1; + } + + /* r0 and r15 (pc) have to be restored later */ + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 0).dirty = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 15).dirty = 1; + + if ((retval = jtag->execute_queue()) != ERROR_OK) + return retval; + + if (arm7_9->post_debug_entry) + arm7_9->post_debug_entry(target); + + return ERROR_OK; +} + +int arm7_9_full_context(target_t *target) +{ + int i; + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + DEBUG(""); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* iterate through processor modes (User, FIQ, IRQ, SVC, ABT, UND) + * SYS shares registers with User, so we don't touch SYS + */ + for(i = 0; i < 6; i++) + { + u32 mask = 0; + u32* reg_p[16]; + int j; + int valid = 1; + + /* check if there are invalid registers in the current mode + */ + for (j = 0; j <= 16; j++) + { + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid == 0) + valid = 0; + } + + if (!valid) + { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= armv4_5_number_to_mode(i); + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + + for (j = 0; j < 15; j++) + { + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid == 0) + { + reg_p[j] = (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).value; + mask |= 1 << j; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).dirty = 0; + } + } + + /* if only the PSR is invalid, mask is all zeroes */ + if (mask) + arm7_9->read_core_regs(target, mask, reg_p); + + /* check if the PSR has to be read */ + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).valid == 0) + { + arm7_9->read_xpsr(target, (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).value, 1); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).dirty = 0; + } + } + } + + /* restore processor mode */ + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG failure"); + exit(-1); + } + return ERROR_OK; +} + +int arm7_9_restore_context(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *reg; + armv4_5_core_reg_t *reg_arch_info; + enum armv4_5_mode current_mode = armv4_5->core_mode; + int i, j; + int dirty; + int mode_change; + + DEBUG(""); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->pre_restore_context) + arm7_9->pre_restore_context(target); + + /* iterate through processor modes (User, FIQ, IRQ, SVC, ABT, UND) + * SYS shares registers with User, so we don't touch SYS + */ + for (i = 0; i < 6; i++) + { + DEBUG("examining %s mode", armv4_5_mode_strings[i]); + dirty = 0; + mode_change = 0; + /* check if there are dirty registers in the current mode + */ + for (j = 0; j <= 16; j++) + { + reg = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j); + reg_arch_info = reg->arch_info; + if (reg->dirty == 1) + { + if (reg->valid == 1) + { + dirty = 1; + DEBUG("examining dirty reg: %s", reg->name); + if ((reg_arch_info->mode != ARMV4_5_MODE_ANY) + && (reg_arch_info->mode != current_mode) + && !((reg_arch_info->mode == ARMV4_5_MODE_USR) && (armv4_5->core_mode == ARMV4_5_MODE_SYS)) + && !((reg_arch_info->mode == ARMV4_5_MODE_SYS) && (armv4_5->core_mode == ARMV4_5_MODE_USR))) + { + mode_change = 1; + DEBUG("require mode change"); + } + } + else + { + ERROR("BUG: dirty register '%s', but no valid data", reg->name); + exit(-1); + } + } + } + + if (dirty) + { + u32 mask = 0x0; + int num_regs = 0; + u32 regs[16]; + + if (mode_change) + { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= armv4_5_number_to_mode(i); + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + current_mode = armv4_5_number_to_mode(i); + } + + for (j = 0; j <= 14; j++) + { + reg = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j); + reg_arch_info = reg->arch_info; + + + if (reg->dirty == 1) + { + regs[j] = buf_get_u32(reg->value, 0, 32); + mask |= 1 << j; + num_regs++; + reg->dirty = 0; + reg->valid = 1; + DEBUG("writing register %i of mode %s with value 0x%8.8x", j, armv4_5_mode_strings[i], regs[j]); + } + } + + if (mask) + { + arm7_9->write_core_regs(target, mask, regs); + } + + reg = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16); + reg_arch_info = reg->arch_info; + if ((reg->dirty) && (reg_arch_info->mode != ARMV4_5_MODE_ANY)) + { + DEBUG("writing SPSR of mode %i with value 0x%8.8x", i, buf_get_u32(reg->value, 0, 32)); + arm7_9->write_xpsr(target, buf_get_u32(reg->value, 0, 32), 1); + } + } + } + + if ((armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty == 0) && (armv4_5->core_mode != current_mode)) + { + /* restore processor mode */ + u32 tmp_cpsr; + + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= armv4_5_number_to_mode(i); + DEBUG("writing lower 8 bit of cpsr with value 0x%2.2x", tmp_cpsr); + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + } + else if (armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty == 1) + { + /* CPSR has been changed, full restore necessary */ + DEBUG("writing cpsr with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + arm7_9->write_xpsr(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), 0); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 0; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + } + + /* restore PC */ + DEBUG("writing PC with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + arm7_9->write_pc(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + armv4_5->core_cache->reg_list[15].dirty = 0; + + if (arm7_9->post_restore_context) + arm7_9->post_restore_context(target); + + return ERROR_OK; +} + +int arm7_9_restart_core(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* set RESTART instruction */ + jtag_add_end_state(TAP_RTI); + arm_jtag_set_instr(jtag_info, 0x4); + + jtag_add_runtest(1, TAP_RTI); + if ((jtag_execute_queue()) != ERROR_OK) + { + exit(-1); + } + + return ERROR_OK; +} + +void arm7_9_enable_watchpoints(struct target_s *target) +{ + watchpoint_t *watchpoint = target->watchpoints; + + while (watchpoint) + { + if (watchpoint->set == 0) + arm7_9_set_watchpoint(target, watchpoint); + watchpoint = watchpoint->next; + } +} + +void arm7_9_enable_breakpoints(struct target_s *target) +{ + breakpoint_t *breakpoint = target->breakpoints; + + /* set any pending breakpoints */ + while (breakpoint) + { + if (breakpoint->set == 0) + arm7_9_set_breakpoint(target, breakpoint); + breakpoint = breakpoint->next; + } +} + +void arm7_9_disable_bkpts_and_wpts(struct target_s *target) +{ + breakpoint_t *breakpoint = target->breakpoints; + watchpoint_t *watchpoint = target->watchpoints; + + /* set any pending breakpoints */ + while (breakpoint) + { + if (breakpoint->set != 0) + arm7_9_unset_breakpoint(target, breakpoint); + breakpoint = breakpoint->next; + } + + while (watchpoint) + { + if (watchpoint->set != 0) + arm7_9_unset_watchpoint(target, watchpoint); + watchpoint = watchpoint->next; + } +} + +int arm7_9_resume(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + breakpoint_t *breakpoint = target->breakpoints; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + DEBUG(""); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!debug_execution) + { + target_free_all_working_areas(target); + } + + /* current = 1: continue on current pc, otherwise continue at
*/ + if (!current) + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, address); + + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) + { + if ((breakpoint = breakpoint_find(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)))) + { + DEBUG("unset breakpoint at 0x%8.8x", breakpoint->address); + arm7_9_unset_breakpoint(target, breakpoint); + + DEBUG("enable single-step"); + arm7_9->enable_single_step(target); + + target->debug_reason = DBG_REASON_SINGLESTEP; + + arm7_9_restore_context(target); + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + arm7_9->branch_resume(target); + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + arm7_9->branch_resume_thumb(target); + } + else + { + ERROR("unhandled core state"); + exit(-1); + } + + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGACK, 1, 0); + embeddedice_write_reg(dbg_ctrl, buf_get_u32(dbg_ctrl->value, 0, dbg_ctrl->size)); + arm7_9_execute_sys_speed(target); + + DEBUG("disable single-step"); + arm7_9->disable_single_step(target); + + arm7_9_debug_entry(target); + DEBUG("new PC after step: 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + + DEBUG("set breakpoint at 0x%8.8x", breakpoint->address); + arm7_9_set_breakpoint(target, breakpoint); + } + } + + /* enable any pending breakpoints and watchpoints */ + arm7_9_enable_breakpoints(target); + arm7_9_enable_watchpoints(target); + + arm7_9_restore_context(target); + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + { + arm7_9->branch_resume(target); + } + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + arm7_9->branch_resume_thumb(target); + } + else + { + ERROR("unhandled core state"); + exit(-1); + } + + /* deassert DBGACK and INTDIS */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGACK, 1, 0); + /* INTDIS only when we really resume, not during debug execution */ + if (!debug_execution) + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_INTDIS, 1, 0); + embeddedice_write_reg(dbg_ctrl, buf_get_u32(dbg_ctrl->value, 0, dbg_ctrl->size)); + + arm7_9_restart_core(target); + + target->debug_reason = DBG_REASON_NOTHALTED; + + if (!debug_execution) + { + /* registers are now invalid */ + armv4_5_invalidate_core_regs(target); + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + } + else + { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + } + + DEBUG("target resumed"); + + return ERROR_OK; +} + +void arm7_9_enable_eice_step(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* setup an inverse breakpoint on the current PC + * - comparator 1 matches the current address + * - rangeout from comparator 1 is connected to comparator 0 rangein + * - comparator 0 matches any address, as long as rangein is low */ + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x100); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], 0x77); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], 0); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], 0xf7); +} + +void arm7_9_disable_eice_step(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE]); +} + +int arm7_9_step(struct target_s *target, int current, u32 address, int handle_breakpoints) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + breakpoint_t *breakpoint = target->breakpoints; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* current = 1: continue on current pc, otherwise continue at
*/ + if (!current) + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, address); + + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) + if ((breakpoint = breakpoint_find(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)))) + arm7_9_unset_breakpoint(target, breakpoint); + + target->debug_reason = DBG_REASON_SINGLESTEP; + + arm7_9_restore_context(target); + + arm7_9->enable_single_step(target); + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + { + arm7_9->branch_resume(target); + } + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + arm7_9->branch_resume_thumb(target); + } + else + { + ERROR("unhandled core state"); + exit(-1); + } + + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + + arm7_9_execute_sys_speed(target); + arm7_9->disable_single_step(target); + + /* registers are now invalid */ + armv4_5_invalidate_core_regs(target); + + arm7_9_debug_entry(target); + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + if (breakpoint) + arm7_9_set_breakpoint(target, breakpoint); + + DEBUG("target stepped"); + + return ERROR_OK; + +} + +int arm7_9_read_core_reg(struct target_s *target, int num, enum armv4_5_mode mode) +{ + u32* reg_p[16]; + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + enum armv4_5_mode reg_mode = ((armv4_5_core_reg_t*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info)->mode; + + if ((num < 0) || (num > 16)) + return ERROR_INVALID_ARGUMENTS; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) + { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= mode; + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + } + + if ((num >= 0) && (num <= 15)) + { + /* read a normal core register */ + reg_p[num] = (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).value; + + arm7_9->read_core_regs(target, 1 << num, reg_p); + } + else + { + /* read a program status register + * if the register mode is MODE_ANY, we read the cpsr, otherwise a spsr + */ + armv4_5_core_reg_t *arch_info = ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info; + int spsr = (arch_info->mode == ARMV4_5_MODE_ANY) ? 0 : 1; + + arm7_9->read_xpsr(target, (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).value, spsr); + } + + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).dirty = 0; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) { + /* restore processor mode */ + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG failure"); + exit(-1); + } + + return ERROR_OK; + +} + +int arm7_9_write_core_reg(struct target_s *target, int num, enum armv4_5_mode mode, u32 value) +{ + u32 reg[16]; + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + enum armv4_5_mode reg_mode = ((armv4_5_core_reg_t*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info)->mode; + + if ((num < 0) || (num > 16)) + return ERROR_INVALID_ARGUMENTS; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= mode; + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + } + + if ((num >= 0) && (num <= 15)) + { + /* write a normal core register */ + reg[num] = value; + + arm7_9->write_core_regs(target, 1 << num, reg); + } + else + { + /* write a program status register + * if the register mode is MODE_ANY, we write the cpsr, otherwise a spsr + */ + armv4_5_core_reg_t *arch_info = ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info; + int spsr = (arch_info->mode == ARMV4_5_MODE_ANY) ? 0 : 1; + + arm7_9->write_xpsr(target, value, spsr); + } + + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).dirty = 0; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) { + /* restore processor mode */ + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG failure"); + exit(-1); + } + + return ERROR_OK; + +} + +int arm7_9_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + u32 reg[16]; + u32 *reg_p[16]; + int num_accesses = 0; + int thisrun_accesses; + u32 *buf32; + u16 *buf16; + u8 *buf8; + int i; + u32 cpsr; + int retval; + int last_reg = 0; + + DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) + return ERROR_INVALID_ARGUMENTS; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + for (i = 0; i < 16; i++) + { + reg_p[i] = ®[i]; + } + + /* load the base register with the address of the first word */ + reg[0] = address; + arm7_9->write_core_regs(target, 0x1, reg); + + switch (size) + { + case 4: + buf32 = (u32*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + arm7_9->load_word_regs(target, reg_list); + arm7_9_execute_sys_speed(target); + + arm7_9->read_core_regs(target, reg_list, reg_p); + jtag_execute_queue(); + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + *(buf32++) = reg[i]; + } + num_accesses += thisrun_accesses; + } + break; + case 2: + buf16 = (u16*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + arm7_9->load_hword_reg(target, i); + arm7_9_execute_sys_speed(target); + } + + arm7_9->read_core_regs(target, reg_list, reg_p); + jtag_execute_queue(); + + for (i = 1; i <= thisrun_accesses; i++) + { + *(buf16++) = reg[i] & 0xffff; + } + num_accesses += thisrun_accesses; + } + break; + case 1: + buf8 = buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + arm7_9->load_byte_reg(target, i); + arm7_9_execute_sys_speed(target); + } + + arm7_9->read_core_regs(target, reg_list, reg_p); + jtag_execute_queue(); + + for (i = 1; i <= thisrun_accesses; i++) + { + *(buf8++) = reg[i] & 0xff; + } + num_accesses += thisrun_accesses; + } + break; + default: + ERROR("BUG: we shouldn't get here"); + exit(-1); + break; + } + + for (i=0; i<=last_reg; i++) + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 1; + + arm7_9->read_xpsr(target, &cpsr, 0); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while reading cpsr"); + exit(-1); + } + + if (((cpsr & 0x1f) == ARMV4_5_MODE_ABT) && (armv4_5->core_mode != ARMV4_5_MODE_ABT)) + { + ERROR("memory read caused data abort"); + + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + + return ERROR_TARGET_DATA_ABORT; + } + + return ERROR_OK; +} + +int arm7_9_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + u32 reg[16]; + int num_accesses = 0; + int thisrun_accesses; + u32 *buf32; + u16 *buf16; + u8 *buf8; + int i; + u32 cpsr; + int retval; + int last_reg = 0; + + DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) + return ERROR_INVALID_ARGUMENTS; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + /* load the base register with the address of the first word */ + reg[0] = address; + arm7_9->write_core_regs(target, 0x1, reg); + + switch (size) + { + case 4: + buf32 = (u32*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + reg[i] = *buf32++; + } + + arm7_9->write_core_regs(target, reg_list, reg); + + arm7_9->store_word_regs(target, reg_list); + + /* fast memory writes are only safe when the target is running + * from a sufficiently high clock (32 kHz is usually too slow) + */ + if (arm7_9->fast_memory_writes) + arm7_9_execute_fast_sys_speed(target); + else + arm7_9_execute_sys_speed(target); + + num_accesses += thisrun_accesses; + } + break; + case 2: + buf16 = (u16*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + reg[i] = *buf16++ & 0xffff; + } + + arm7_9->write_core_regs(target, reg_list, reg); + + for (i = 1; i <= thisrun_accesses; i++) + { + arm7_9->store_hword_reg(target, i); + + /* fast memory writes are only safe when the target is running + * from a sufficiently high clock (32 kHz is usually too slow) + */ + if (arm7_9->fast_memory_writes) + arm7_9_execute_fast_sys_speed(target); + else + arm7_9_execute_sys_speed(target); + } + + num_accesses += thisrun_accesses; + } + break; + case 1: + buf8 = buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + reg[i] = *buf8++ & 0xff; + } + + arm7_9->write_core_regs(target, reg_list, reg); + + for (i = 1; i <= thisrun_accesses; i++) + { + arm7_9->store_byte_reg(target, i); + /* fast memory writes are only safe when the target is running + * from a sufficiently high clock (32 kHz is usually too slow) + */ + if (arm7_9->fast_memory_writes) + arm7_9_execute_fast_sys_speed(target); + else + arm7_9_execute_sys_speed(target); + } + + num_accesses += thisrun_accesses; + } + break; + default: + ERROR("BUG: we shouldn't get here"); + exit(-1); + break; + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing target memory"); + exit(-1); + } + + for (i=0; i<=last_reg; i++) + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 1; + + arm7_9->read_xpsr(target, &cpsr, 0); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while reading cpsr"); + exit(-1); + } + + if (((cpsr & 0x1f) == ARMV4_5_MODE_ABT) && (armv4_5->core_mode != ARMV4_5_MODE_ABT)) + { + ERROR("memory write caused data abort"); + + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + + return ERROR_TARGET_DATA_ABORT; + } + + return ERROR_OK; +} + +int arm7_9_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + enum armv4_5_state core_state = armv4_5->core_state; + u32 r0 = buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32); + u32 r1 = buf_get_u32(armv4_5->core_cache->reg_list[1].value, 0, 32); + u32 pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + int i; + + u32 dcc_code[] = + { + /* MRC TST BNE MRC STR B */ + 0xee101e10, 0xe3110001, 0x0afffffc, 0xee111e10, 0xe4801004, 0xeafffff9 + }; + + if (!arm7_9->dcc_downloads) + return target->type->write_memory(target, address, 4, count, buffer); + + /* regrab previously allocated working_area, or allocate a new one */ + if (!arm7_9->dcc_working_area) + { + /* make sure we have a working area */ + if (target_alloc_working_area(target, 24, &arm7_9->dcc_working_area) != ERROR_OK) + { + INFO("no working area available, falling back to memory writes"); + return target->type->write_memory(target, address, 4, count, buffer); + } + + /* write DCC code to working area */ + target->type->write_memory(target, arm7_9->dcc_working_area->address, 4, 6, (u8*)dcc_code); + } + + buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, address); + armv4_5->core_cache->reg_list[0].valid = 1; + armv4_5->core_cache->reg_list[0].dirty = 1; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + arm7_9_resume(target, 0, arm7_9->dcc_working_area->address, 1, 1); + + for (i = 0; i < count; i++) + { + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_COMMS_DATA], buf_get_u32(buffer, 0, 32)); + buffer += 4; + } + + target->type->halt(target); + + while (target->state != TARGET_HALTED) + target->type->poll(target); + + /* restore target state */ + buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, r0); + armv4_5->core_cache->reg_list[0].valid = 1; + armv4_5->core_cache->reg_list[0].dirty = 1; + buf_set_u32(armv4_5->core_cache->reg_list[1].value, 0, 32, r1); + armv4_5->core_cache->reg_list[1].valid = 1; + armv4_5->core_cache->reg_list[1].dirty = 1; + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, pc); + armv4_5->core_cache->reg_list[15].valid = 1; + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_state = core_state; + + return ERROR_OK; +} + +int arm7_9_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *arm7_9_cmd; + + arm7_9_cmd = register_command(cmd_ctx, NULL, "arm7_9", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, arm7_9_cmd, "write_xpsr", handle_arm7_9_write_xpsr_command, COMMAND_EXEC, "write program status register "); + register_command(cmd_ctx, arm7_9_cmd, "write_xpsr_im8", handle_arm7_9_write_xpsr_im8_command, COMMAND_EXEC, "write program status register <8bit immediate> "); + + register_command(cmd_ctx, arm7_9_cmd, "write_core_reg", handle_arm7_9_write_core_reg_command, COMMAND_EXEC, "write core register "); + + register_command(cmd_ctx, arm7_9_cmd, "sw_bkpts", handle_arm7_9_sw_bkpts_command, COMMAND_EXEC, "support for software breakpoints "); + register_command(cmd_ctx, arm7_9_cmd, "force_hw_bkpts", handle_arm7_9_force_hw_bkpts_command, COMMAND_EXEC, "use hardware breakpoints for all breakpoints (disables sw breakpoint support) "); + register_command(cmd_ctx, arm7_9_cmd, "dbgrq", handle_arm7_9_dbgrq_command, + COMMAND_ANY, "use EmbeddedICE dbgrq instead of breakpoint for target halt requests "); + register_command(cmd_ctx, arm7_9_cmd, "fast_writes", handle_arm7_9_fast_writes_command, + COMMAND_ANY, "use fast memory writes instead of slower but potentially unsafe slow writes "); + register_command(cmd_ctx, arm7_9_cmd, "dcc_downloads", handle_arm7_9_dcc_downloads_command, + COMMAND_ANY, "use DCC downloads for larger memory writes "); + + armv4_5_register_commands(cmd_ctx); + + return ERROR_OK; +} + +int handle_arm7_9_write_xpsr_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 value; + int spsr; + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "can't write registers while running"); + return ERROR_OK; + } + + if (argc < 2) + { + command_print(cmd_ctx, "usage: write_xpsr "); + return ERROR_OK; + } + + value = strtoul(args[0], NULL, 0); + spsr = strtol(args[1], NULL, 0); + + arm7_9->write_xpsr(target, value, spsr); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing to xpsr"); + exit(-1); + } + + return ERROR_OK; +} + +int handle_arm7_9_write_xpsr_im8_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 value; + int rotate; + int spsr; + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "can't write registers while running"); + return ERROR_OK; + } + + if (argc < 3) + { + command_print(cmd_ctx, "usage: write_xpsr_im8 "); + return ERROR_OK; + } + + value = strtoul(args[0], NULL, 0); + rotate = strtol(args[1], NULL, 0); + spsr = strtol(args[2], NULL, 0); + + arm7_9->write_xpsr_im8(target, value, rotate, spsr); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing 8-bit immediate to xpsr"); + exit(-1); + } + + return ERROR_OK; +} + +int handle_arm7_9_write_core_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 value; + u32 mode; + int num; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "can't write registers while running"); + return ERROR_OK; + } + + if (argc < 3) + { + command_print(cmd_ctx, "usage: write_core_reg "); + return ERROR_OK; + } + + num = strtol(args[0], NULL, 0); + mode = strtoul(args[1], NULL, 0); + value = strtoul(args[2], NULL, 0); + + arm7_9_write_core_reg(target, num, mode, value); + + return ERROR_OK; +} + +int handle_arm7_9_sw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc == 0) + { + command_print(cmd_ctx, "software breakpoints %s", (arm7_9->sw_bkpts_enabled) ? "enabled" : "disabled"); + return ERROR_OK; + } + + if (strcmp("enable", args[0]) == 0) + { + if (arm7_9->sw_bkpts_use_wp) + { + arm7_9_enable_sw_bkpts(target); + } + else + { + arm7_9->sw_bkpts_enabled = 1; + } + } + else if (strcmp("disable", args[0]) == 0) + { + if (arm7_9->sw_bkpts_use_wp) + { + arm7_9_disable_sw_bkpts(target); + } + else + { + arm7_9->sw_bkpts_enabled = 0; + } + } + else + { + command_print(cmd_ctx, "usage: arm7_9 sw_bkpts "); + } + + command_print(cmd_ctx, "software breakpoints %s", (arm7_9->sw_bkpts_enabled) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_force_hw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if ((argc >= 1) && (strcmp("enable", args[0]) == 0)) + { + arm7_9->force_hw_bkpts = 1; + if (arm7_9->sw_bkpts_use_wp) + { + arm7_9_disable_sw_bkpts(target); + } + } + else if ((argc >= 1) && (strcmp("disable", args[0]) == 0)) + { + arm7_9->force_hw_bkpts = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 force_hw_bkpts "); + } + + command_print(cmd_ctx, "force hardware breakpoints %s", (arm7_9->force_hw_bkpts) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_dbgrq_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp("enable", args[0]) == 0) + { + arm7_9->use_dbgrq = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + arm7_9->use_dbgrq = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 dbgrq "); + } + } + + command_print(cmd_ctx, "use of EmbeddedICE dbgrq instead of breakpoint for target halt %s", (arm7_9->use_dbgrq) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_fast_writes_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp("enable", args[0]) == 0) + { + arm7_9->fast_memory_writes = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + arm7_9->fast_memory_writes = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 fast_writes "); + } + } + + command_print(cmd_ctx, "fast memory writes are %s", (arm7_9->fast_memory_writes) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_dcc_downloads_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp("enable", args[0]) == 0) + { + arm7_9->dcc_downloads = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + arm7_9->dcc_downloads = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 dcc_downloads "); + } + } + + command_print(cmd_ctx, "dcc downloads are %s", (arm7_9->dcc_downloads) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int arm7_9_init_arch_info(target_t *target, arm7_9_common_t *arm7_9) +{ + armv4_5_common_t *armv4_5 = &arm7_9->armv4_5_common; + + arm7_9->common_magic = ARM7_9_COMMON_MAGIC; + + arm_jtag_setup_connection(&arm7_9->jtag_info); + arm7_9->wp_available = 2; + arm7_9->wp0_used = 0; + arm7_9->wp1_used = 0; + arm7_9->force_hw_bkpts = 0; + arm7_9->use_dbgrq = 0; + arm7_9->has_etm = 0; + + arm7_9->reinit_embeddedice = 0; + + arm7_9->dcc_working_area = NULL; + + arm7_9->fast_memory_writes = 0; + arm7_9->dcc_downloads = 0; + + jtag_register_event_callback(arm7_9_jtag_callback, target); + + armv4_5->arch_info = arm7_9; + armv4_5->read_core_reg = arm7_9_read_core_reg; + armv4_5->write_core_reg = arm7_9_write_core_reg; + armv4_5->full_context = arm7_9_full_context; + + armv4_5_init_arch_info(target, armv4_5); + + return ERROR_OK; +} diff --git a/src/target/arm7_9_common.h b/src/target/arm7_9_common.h new file mode 100644 index 00000000..f03ae496 --- /dev/null +++ b/src/target/arm7_9_common.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARM7_9_COMMON_H +#define ARM7_9_COMMON_H + +#include "armv4_5.h" +#include "arm_jtag.h" +#include "breakpoints.h" +#include "target.h" + +#define ARM7_9_COMMON_MAGIC 0x0a790a79 + +typedef struct arm7_9_common_s +{ + int common_magic; + + arm_jtag_t jtag_info; + reg_cache_t *eice_cache; + reg_cache_t *etm_cache; + + u32 arm_bkpt; + u16 thumb_bkpt; + int sw_bkpts_use_wp; + int wp_available; + int wp0_used; + int wp1_used; + int sw_bkpts_enabled; + int force_hw_bkpts; + int dbgreq_adjust_pc; + int use_dbgrq; + int has_etm; + + int reinit_embeddedice; + + struct working_area_s *dcc_working_area; + + int fast_memory_writes; + int dcc_downloads; + + int (*examine_debug_reason)(target_t *target); + + void (*change_to_arm)(target_t *target, u32 *r0, u32 *pc); + + void (*read_core_regs)(target_t *target, u32 mask, u32* core_regs[16]); + void (*read_xpsr)(target_t *target, u32 *xpsr, int spsr); + + void (*write_xpsr)(target_t *target, u32 xpsr, int spsr); + void (*write_xpsr_im8)(target_t *target, u8 xpsr_im, int rot, int spsr); + void (*write_core_regs)(target_t *target, u32 mask, u32 core_regs[16]); + + void (*load_word_regs)(target_t *target, u32 mask); + void (*load_hword_reg)(target_t *target, int num); + void (*load_byte_reg)(target_t *target, int num); + + void (*store_word_regs)(target_t *target, u32 mask); + void (*store_hword_reg)(target_t *target, int num); + void (*store_byte_reg)(target_t *target, int num); + + void (*write_pc)(target_t *target, u32 pc); + void (*branch_resume)(target_t *target); + void (*branch_resume_thumb)(target_t *target); + + void (*enable_single_step)(target_t *target); + void (*disable_single_step)(target_t *target); + + void (*pre_debug_entry)(target_t *target); + void (*post_debug_entry)(target_t *target); + + void (*pre_restore_context)(target_t *target); + void (*post_restore_context)(target_t *target); + + armv4_5_common_t armv4_5_common; + void *arch_info; + +} arm7_9_common_t; + +int arm7_9_register_commands(struct command_context_s *cmd_ctx); + +enum target_state arm7_9_poll(target_t *target); + +int arm7_9_assert_reset(target_t *target); +int arm7_9_deassert_reset(target_t *target); +int arm7_9_reset_request_halt(target_t *target); +int arm7_9_early_halt(target_t *target); +int arm7_9_soft_reset_halt(struct target_s *target); + +int arm7_9_halt(target_t *target); +int arm7_9_debug_entry(target_t *target); +int arm7_9_full_context(target_t *target); +int arm7_9_resume(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution); +int arm7_9_step(struct target_s *target, int current, u32 address, int handle_breakpoints); +int arm7_9_read_core_reg(struct target_s *target, int num, enum armv4_5_mode mode); +int arm7_9_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm7_9_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm7_9_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffer); + +int arm7_9_run_algorithm(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_prams, reg_param_t *reg_param, u32 entry_point, void *arch_info); + +int arm7_9_add_breakpoint(struct target_s *target, u32 address, u32 length, enum breakpoint_type type); +int arm7_9_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint); +int arm7_9_add_watchpoint(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw); +int arm7_9_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint); + +void arm7_9_enable_eice_step(target_t *target); +void arm7_9_disable_eice_step(target_t *target); + +int arm7_9_execute_sys_speed(struct target_s *target); + +int arm7_9_init_arch_info(target_t *target, arm7_9_common_t *arm7_9); + + +#endif /* ARM7_9_COMMON_H */ diff --git a/src/target/arm7tdmi.c b/src/target/arm7tdmi.c new file mode 100644 index 00000000..c2079477 --- /dev/null +++ b/src/target/arm7tdmi.c @@ -0,0 +1,780 @@ +/*************************************************************************** + * 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 "arm7tdmi.h" + +#include "arm7_9_common.h" +#include "register.h" +#include "target.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "etm.h" +#include "log.h" +#include "jtag.h" +#include "arm_jtag.h" + +#include +#include + +#if 0 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm7tdmi_register_commands(struct command_context_s *cmd_ctx); + +/* forward declarations */ +int arm7tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm7tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm7tdmi_quit(); + +/* target function declarations */ +enum target_state arm7tdmi_poll(struct target_s *target); +int arm7tdmi_halt(target_t *target); +int arm7tdmi_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + +target_type_t arm7tdmi_target = +{ + .name = "arm7tdmi", + + .poll = arm7_9_poll, + .arch_state = armv4_5_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm7_9_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm7_9_read_memory, + .write_memory = arm7_9_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm7tdmi_register_commands, + .target_command = arm7tdmi_target_command, + .init_target = arm7tdmi_init_target, + .quit = arm7tdmi_quit +}; + +int arm7tdmi_examine_debug_reason(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* only check the debug reason if we don't know it already */ + if ((target->debug_reason != DBG_REASON_DBGRQ) + && (target->debug_reason != DBG_REASON_SINGLESTEP)) + { + scan_field_t fields[2]; + u8 databus[4]; + u8 breakpoint; + + jtag_add_end_state(TAP_PD); + + fields[0].device = arm7_9->jtag_info.chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = &breakpoint; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = arm7_9->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = databus; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + arm_jtag_scann(&arm7_9->jtag_info, 0x1); + arm_jtag_set_instr(&arm7_9->jtag_info, arm7_9->jtag_info.intest_instr); + + jtag_add_dr_scan(2, fields, TAP_PD); + jtag_execute_queue(); + + fields[0].in_value = NULL; + fields[0].out_value = &breakpoint; + fields[1].in_value = NULL; + fields[1].out_value = databus; + + jtag_add_dr_scan(2, fields, TAP_PD); + + if (breakpoint & 1) + target->debug_reason = DBG_REASON_WATCHPOINT; + else + target->debug_reason = DBG_REASON_BREAKPOINT; + } + + return ERROR_OK; +} + +/* put an instruction in the ARM7TDMI pipeline or write the data bus, and optionally read data */ +int arm7tdmi_clock_out(arm_jtag_t *jtag_info, u32 out, u32 *in, int breakpoint) +{ + scan_field_t fields[2]; + u8 out_buf[4]; + u8 breakpoint_buf; + + out = flip_u32(out, 32); + buf_set_u32(out_buf, 0, 32, out); + buf_set_u32(&breakpoint_buf, 0, 1, breakpoint); + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &breakpoint_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = out_buf; + fields[1].out_mask = NULL; + if (in) + { + fields[1].in_value = (u8*)in; + fields[1].in_handler = arm_jtag_buf_to_u32_flip; + fields[1].in_handler_priv = in; + } else + { + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + } + + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + jtag_add_dr_scan(2, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ +{ + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("out: 0x%8.8x, in: %s", flip_u32(out, 32), in_string); + free(in_string); + } + else + DEBUG("out: 0x%8.8x", flip_u32(out, 32)); +} +#endif + + return ERROR_OK; +} + +/* put an instruction in the ARM7TDMI pipeline, and optionally read data */ +int arm7tdmi_clock_data_in(arm_jtag_t *jtag_info, u32 *in) +{ + scan_field_t fields[2]; + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = (u8*)in; + fields[1].in_handler = arm_jtag_buf_to_u32_flip; + fields[1].in_handler_priv = in; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + jtag_add_dr_scan(2, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ +{ + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("in: %s", in_string); + free(in_string); + } +} +#endif + + return ERROR_OK; +} + +void arm7tdmi_change_to_arm(target_t *target, u32 *r0, u32 *pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* save r0 before using it and put system in ARM state + * to allow common handling of ARM and THUMB debugging */ + + /* fetch STR r0, [r0] */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* nothing fetched, STR r0, [r0] in Execute (2) */ + arm7tdmi_clock_data_in(jtag_info, r0); + + /* MOV r0, r15 fetched, STR in Decode */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV(0, 15), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* nothing fetched, STR r0, [r0] in Execute (2) */ + arm7tdmi_clock_data_in(jtag_info, pc); + + /* fetch MOV */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), NULL, 0); + + /* fetch BX */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_BX(0), NULL, 0); + /* NOP fetched, BX in Decode, MOV in Execute */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* NOP fetched, BX in Execute (1) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + + jtag_execute_queue(); + + /* fix program counter: + * MOV r0, r15 was the 4th instruction (+6) + * reading PC in Thumb state gives address of instruction + 4 + */ + *pc -= 0xa; + +} + +void arm7tdmi_read_core_regs(target_t *target, u32 mask, u32* core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* STMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), NULL, 0); + + /* fetch NOP, STM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, STM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, STM still in EXECUTE (1+i cycle) */ + arm7tdmi_clock_data_in(jtag_info, core_regs[i]); + } + +} + +void arm7tdmi_read_xpsr(target_t *target, u32 *xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* MRS r0, cpsr */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MRS(0, spsr & 1), NULL, 0); + + /* STR r0, [r15] */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 15), NULL, 0); + /* fetch NOP, STR in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, STR in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, STR still in EXECUTE (2nd cycle) */ + arm7tdmi_clock_data_in(jtag_info, xpsr); + +} + +void arm7tdmi_write_xpsr(target_t *target, u32 xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr: %8.8x, spsr: %i", xpsr, spsr); + + /* MSR1 fetched */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr & 0xff, 0, 1, spsr), NULL, 0); + /* MSR2 fetched, MSR1 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff00) >> 8, 0xc, 2, spsr), NULL, 0); + /* MSR3 fetched, MSR1 in EXECUTE (1), MSR2 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff0000) >> 16, 0x8, 4, spsr), NULL, 0); + /* nothing fetched, MSR1 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* MSR4 fetched, MSR2 in EXECUTE (1), MSR3 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff000000) >> 24, 0x4, 8, spsr), NULL, 0); + /* nothing fetched, MSR2 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* NOP fetched, MSR3 in EXECUTE (1), MSR4 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, MSR3 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* NOP fetched, MSR4 in EXECUTE (1) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, MSR4 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); +} + +void arm7tdmi_write_xpsr_im8(target_t *target, u8 xpsr_im, int rot, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr_im: %2.2x, rot: %i, spsr: %i", xpsr_im, rot, spsr); + + /* MSR fetched */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr_im, rot, 1, spsr), NULL, 0); + /* NOP fetched, MSR in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* NOP fetched, MSR in EXECUTE (1) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, MSR in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + +} + +void arm7tdmi_write_core_regs(target_t *target, u32 mask, u32 core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 0), NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, LDM still in EXECUTE (1+i cycle) */ + arm7tdmi_clock_out(jtag_info, core_regs[i], NULL, 0); + } + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + +} + +void arm7tdmi_load_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load-multiple into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 1), NULL, 0); + +} + +void arm7tdmi_load_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load half-word into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRH_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_load_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load byte into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRB_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_store_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store-multiple into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask, 0, 1), NULL, 0); + +} + +void arm7tdmi_store_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store half-word into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STRH_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_store_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store byte into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STRB_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_write_pc(target_t *target, u32 pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x8000, 0, 0), NULL, 0); + /* fetch NOP, LDM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (1st cycle) load register */ + arm7tdmi_clock_out(jtag_info, pc, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) load register */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) load register */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (4th cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (5th cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); +} + +void arm7tdmi_branch_resume(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_B(0xfffffa, 0), NULL, 0); + +} + +void arm7tdmi_branch_resume_thumb(target_t *target) +{ + DEBUG(""); + + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* LDMIA r0, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x1, 0, 0), NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) */ + arm7tdmi_clock_out(jtag_info, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) | 1, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + /* Branch and eXchange */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_BX(0), NULL, 0); + + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + /* target is now in Thumb state */ + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + /* target is now in Thumb state */ + embeddedice_read_reg(dbg_stat); + + /* clean r0 bits to avoid alignment problems */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), NULL, 0); + /* load r0 value, MOV_IM in Decode*/ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_LDR(0, 0), NULL, 0); + /* fetch NOP, LDR in Decode, MOV_IM in Execute */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* fetch NOP, LDR in Execute */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (2nd cycle) */ + arm7tdmi_clock_out(jtag_info, buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32), NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (3rd cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_B(0x7f7), NULL, 0); + +} + +void arm7tdmi_build_reg_cache(target_t *target) +{ + reg_cache_t **cache_p = register_get_last_cache_p(&target->reg_cache); + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + arm7tdmi_common_t *arch_info = arm7_9->arch_info; + + + (*cache_p) = armv4_5_build_reg_cache(target, armv4_5); + armv4_5->core_cache = (*cache_p); + + (*cache_p)->next = embeddedice_build_reg_cache(target, jtag_info, 0); + arm7_9->eice_cache = (*cache_p)->next; + + if (arm7_9->has_etm) + { + (*cache_p)->next->next = etm_build_reg_cache(target, jtag_info, 0); + arm7_9->etm_cache = (*cache_p)->next->next; + } + + if (arch_info->has_monitor_mode) + (*cache_p)->next->reg_list[0].size = 6; + else + (*cache_p)->next->reg_list[0].size = 3; + + (*cache_p)->next->reg_list[1].size = 5; + +} + +int arm7tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + + arm7tdmi_build_reg_cache(target); + + return ERROR_OK; + +} + +int arm7tdmi_quit() +{ + + return ERROR_OK; +} + +int arm7tdmi_init_arch_info(target_t *target, arm7tdmi_common_t *arm7tdmi, int chain_pos, char *variant) +{ + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + int has_etm = 0; + + arm7_9 = &arm7tdmi->arm7_9_common; + armv4_5 = &arm7_9->armv4_5_common; + + /* prepare JTAG information for the new target */ + arm7_9->jtag_info.chain_pos = chain_pos; + arm7_9->jtag_info.scann_size = 4; + + /* register arch-specific functions */ + arm7_9->examine_debug_reason = arm7tdmi_examine_debug_reason; + arm7_9->change_to_arm = arm7tdmi_change_to_arm; + arm7_9->read_core_regs = arm7tdmi_read_core_regs; + arm7_9->read_xpsr = arm7tdmi_read_xpsr; + + arm7_9->write_xpsr = arm7tdmi_write_xpsr; + arm7_9->write_xpsr_im8 = arm7tdmi_write_xpsr_im8; + arm7_9->write_core_regs = arm7tdmi_write_core_regs; + + arm7_9->load_word_regs = arm7tdmi_load_word_regs; + arm7_9->load_hword_reg = arm7tdmi_load_hword_reg; + arm7_9->load_byte_reg = arm7tdmi_load_byte_reg; + + arm7_9->store_word_regs = arm7tdmi_store_word_regs; + arm7_9->store_hword_reg = arm7tdmi_store_hword_reg; + arm7_9->store_byte_reg = arm7tdmi_store_byte_reg; + + arm7_9->write_pc = arm7tdmi_write_pc; + arm7_9->branch_resume = arm7tdmi_branch_resume; + arm7_9->branch_resume_thumb = arm7tdmi_branch_resume_thumb; + + arm7_9->enable_single_step = arm7_9_enable_eice_step; + arm7_9->disable_single_step = arm7_9_disable_eice_step; + + arm7_9->pre_debug_entry = NULL; + arm7_9->post_debug_entry = NULL; + + arm7_9->pre_restore_context = NULL; + arm7_9->post_restore_context = NULL; + + /* initialize arch-specific breakpoint handling */ + buf_set_u32((u8*)(&arm7_9->arm_bkpt), 0, 32, 0xdeeedeee); + buf_set_u32((u8*)(&arm7_9->thumb_bkpt), 0, 16, 0xdeee); + + arm7_9->sw_bkpts_use_wp = 1; + arm7_9->sw_bkpts_enabled = 0; + arm7_9->dbgreq_adjust_pc = 2; + arm7_9->arch_info = arm7tdmi; + + arm7tdmi->has_monitor_mode = 0; + arm7tdmi->arch_info = NULL; + arm7tdmi->common_magic = ARM7TDMI_COMMON_MAGIC; + + if (variant) + { + if (strcmp(variant, "arm7tdmi-s_r4") == 0) + arm7tdmi->has_monitor_mode = 1; + else if (strcmp(variant, "arm7tdmi_r4") == 0) + arm7tdmi->has_monitor_mode = 1; + else if (strcmp(variant, "lpc2000") == 0) + { + arm7tdmi->has_monitor_mode = 1; + has_etm = 1; + } + arm7tdmi->variant = strdup(variant); + } + else + arm7tdmi->variant = strdup(""); + + arm7_9_init_arch_info(target, arm7_9); + + arm7_9->has_etm = has_etm; + + return ERROR_OK; +} + +/* target arm7tdmi */ +int arm7tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm7tdmi_common_t *arm7tdmi = malloc(sizeof(arm7tdmi_common_t)); + + if (argc < 4) + { + ERROR("'target arm7tdmi' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[2], NULL, 0); + + if (argc >= 5) + variant = args[4]; + + arm7tdmi_init_arch_info(target, arm7tdmi, chain_pos, variant); + + return ERROR_OK; +} + +int arm7tdmi_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + + retval = arm7_9_register_commands(cmd_ctx); + + return ERROR_OK; + +} + diff --git a/src/target/arm7tdmi.h b/src/target/arm7tdmi.h new file mode 100644 index 00000000..ca2df8b6 --- /dev/null +++ b/src/target/arm7tdmi.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARM7TDMI_H +#define ARM7TDMI_H + +#include "target.h" +#include "register.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm7_9_common.h" + +#define ARM7TDMI_COMMON_MAGIC 0x00a700a7 + +typedef struct arm7tdmi_common_s +{ + int common_magic; + char *variant; + int has_monitor_mode; + void *arch_info; + arm7_9_common_t arm7_9_common; +} arm7tdmi_common_t; + +int arm7tdmi_register_commands(struct command_context_s *cmd_ctx); +int arm7tdmi_init_arch_info(target_t *target, arm7tdmi_common_t *arm7tdmi, int chain_pos, char *variant); +int arm7tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); + + +#endif /* ARM7TDMI_H */ diff --git a/src/target/arm920t.c b/src/target/arm920t.c new file mode 100644 index 00000000..eb0fa7df --- /dev/null +++ b/src/target/arm920t.c @@ -0,0 +1,967 @@ +/*************************************************************************** + * 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 "arm920t.h" +#include "jtag.h" +#include "log.h" + +#include +#include + +#if 0 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm920t_register_commands(struct command_context_s *cmd_ctx); + +int arm920t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_cp15i_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_virt2phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_cache_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_md_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_mw_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* forward declarations */ +int arm920t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm920t_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm920t_quit(); +int arm920t_arch_state(struct target_s *target, char *buf, int buf_size); +int arm920t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm920t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm920t_soft_reset_halt(struct target_s *target); + +target_type_t arm920t_target = +{ + .name = "arm920t", + + .poll = arm7_9_poll, + .arch_state = arm920t_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm920t_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm920t_read_memory, + .write_memory = arm920t_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm920t_register_commands, + .target_command = arm920t_target_command, + .init_target = arm920t_init_target, + .quit = arm920t_quit +}; + +int arm920t_read_cp15_physical(target_t *target, int reg_addr, u32 *value) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + scan_field_t fields[4]; + u8 access_type_buf = 1; + u8 reg_addr_buf = reg_addr & 0x3f; + u8 nr_w_buf = 0; + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + fields[1].in_value = (u8*)value; + + jtag_add_dr_scan(4, fields, -1); + + return ERROR_OK; +} + +int arm920t_write_cp15_physical(target_t *target, int reg_addr, u32 value) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + scan_field_t fields[4]; + u8 access_type_buf = 1; + u8 reg_addr_buf = reg_addr & 0x3f; + u8 nr_w_buf = 1; + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = (u8*)&value; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + return ERROR_OK; +} + +int arm920t_read_cp15_interpreted(target_t *target, u32 opcode, u32 *value) +{ + u32 cp15c15 = 0x0; + scan_field_t fields[4]; + u8 access_type_buf = 0; /* interpreted access */ + u8 reg_addr_buf = 0x0; + u8 nr_w_buf = 0; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + u32* context_p[1]; + + /* read-modify-write CP15 test state register + * to enable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 |= 1; /* set interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = (u8*)&opcode; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDR(0, 15), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + arm7_9_execute_sys_speed(target); + jtag_execute_queue(); + + /* read-modify-write CP15 test state register + * to disable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 &= ~1U; /* clear interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + context_p[0] = value; + arm9tdmi_read_core_regs(target, 0x1, context_p); + jtag_execute_queue(); + + DEBUG("opcode: %8.8x, value: %8.8x", opcode, *value); + + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 0).dirty = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 15).dirty = 1; + + return ERROR_OK; +} + +int arm920t_write_cp15_interpreted(target_t *target, u32 opcode, u32 value, u32 address) +{ + u32 cp15c15 = 0x0; + scan_field_t fields[4]; + u8 access_type_buf = 0; /* interpreted access */ + u8 reg_addr_buf = 0x0; + u8 nr_w_buf = 0; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + u32 regs[2]; + + regs[0] = value; + regs[1] = address; + + arm9tdmi_write_core_regs(target, 0x3, regs); + + /* read-modify-write CP15 test state register + * to enable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 |= 1; /* set interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = (u8*)&opcode; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + arm7_9_execute_sys_speed(target); + jtag_execute_queue(); + + /* read-modify-write CP15 test state register + * to disable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 &= ~1U; /* set interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + DEBUG("opcode: %8.8x, value: %8.8x, address: %8.8x", opcode, value, address); + + return ERROR_OK; +} + +u32 arm920t_get_ttb(target_t *target) +{ + int retval; + u32 ttb = 0x0; + + if ((retval = arm920t_read_cp15_interpreted(target, 0xeebf0f51, &ttb)) != ERROR_OK) + return retval; + + return ttb; +} + +void arm920t_disable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm920t_read_cp15_physical(target, 0x2, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control &= ~0x1U; + + if (d_u_cache) + cp15_control &= ~0x4U; + + if (i_cache) + cp15_control &= ~0x1000U; + + arm920t_write_cp15_physical(target, 0x2, cp15_control); +} + +void arm920t_enable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm920t_read_cp15_physical(target, 0x2, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control |= 0x1U; + + if (d_u_cache) + cp15_control |= 0x4U; + + if (i_cache) + cp15_control |= 0x1000U; + + arm920t_write_cp15_physical(target, 0x2, cp15_control); +} + +void arm920t_post_debug_entry(target_t *target) +{ + u32 cp15c15; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + /* examine cp15 control reg */ + arm920t_read_cp15_physical(target, 0x2, &arm920t->cp15_control_reg); + jtag_execute_queue(); + DEBUG("cp15_control_reg: %8.8x", arm920t->cp15_control_reg); + + if (arm920t->armv4_5_mmu.armv4_5_cache.ctype == -1) + { + u32 cache_type_reg; + /* identify caches */ + arm920t_read_cp15_physical(target, 0x1, &cache_type_reg); + jtag_execute_queue(); + armv4_5_identify_cache(cache_type_reg, &arm920t->armv4_5_mmu.armv4_5_cache); + } + + arm920t->armv4_5_mmu.mmu_enabled = (arm920t->cp15_control_reg & 0x1U) ? 1 : 0; + arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = (arm920t->cp15_control_reg & 0x4U) ? 1 : 0; + arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = (arm920t->cp15_control_reg & 0x1000U) ? 1 : 0; + + /* save i/d fault status and address register */ + arm920t_read_cp15_interpreted(target, 0xee150f10, &arm920t->d_fsr); + arm920t_read_cp15_interpreted(target, 0xee150f30, &arm920t->i_fsr); + arm920t_read_cp15_interpreted(target, 0xee160f10, &arm920t->d_far); + arm920t_read_cp15_interpreted(target, 0xee160f30, &arm920t->i_far); + + /* read-modify-write CP15 test state register + * to disable I/D-cache linefills */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 |= 0x600; + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + +} + +void arm920t_pre_restore_context(target_t *target) +{ + u32 cp15c15; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + /* restore i/d fault status and address register */ + arm920t_write_cp15_interpreted(target, 0xee050f10, arm920t->d_fsr, 0x0); + arm920t_write_cp15_interpreted(target, 0xee050f30, arm920t->i_fsr, 0x0); + arm920t_write_cp15_interpreted(target, 0xee060f10, arm920t->d_far, 0x0); + arm920t_write_cp15_interpreted(target, 0xee060f30, arm920t->i_far, 0x0); + + /* read-modify-write CP15 test state register + * to reenable I/D-cache linefills */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 &= ~0x600U; + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + +} + +int arm920t_get_arch_pointers(target_t *target, armv4_5_common_t **armv4_5_p, arm7_9_common_t **arm7_9_p, arm9tdmi_common_t **arm9tdmi_p, arm920t_common_t **arm920t_p) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + return -1; + } + + arm7_9 = armv4_5->arch_info; + if (arm7_9->common_magic != ARM7_9_COMMON_MAGIC) + { + return -1; + } + + arm9tdmi = arm7_9->arch_info; + if (arm9tdmi->common_magic != ARM9TDMI_COMMON_MAGIC) + { + return -1; + } + + arm920t = arm9tdmi->arch_info; + if (arm920t->common_magic != ARM920T_COMMON_MAGIC) + { + return -1; + } + + *armv4_5_p = armv4_5; + *arm7_9_p = arm7_9; + *arm9tdmi_p = arm9tdmi; + *arm920t_p = arm920t; + + return ERROR_OK; +} + +int arm920t_arch_state(struct target_s *target, char *buf, int buf_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + char *state[] = + { + "disabled", "enabled" + }; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("BUG: called for a non-ARMv4/5 target"); + exit(-1); + } + + snprintf(buf, buf_size, + "target halted in %s state due to %s, current mode: %s\n" + "cpsr: 0x%8.8x pc: 0x%8.8x\n" + "MMU: %s, D-Cache: %s, I-Cache: %s", + armv4_5_state_strings[armv4_5->core_state], + target_debug_reason_strings[target->debug_reason], + armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)], + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32), + state[arm920t->armv4_5_mmu.mmu_enabled], + state[arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled], + state[arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled]); + + return ERROR_OK; +} + +int arm920t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + retval = arm7_9_read_memory(target, address, size, count, buffer); + + return retval; +} + +int arm920t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + if ((retval = arm7_9_write_memory(target, address, size, count, buffer)) != ERROR_OK) + return retval; + + if (((size == 4) || (size == 2)) && (count == 1)) + { + if (arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled) + { + DEBUG("D-Cache enabled, writing through to main memory"); + u32 pa, cb, ap; + int type, domain; + + pa = armv4_5_mmu_translate_va(target, &arm920t->armv4_5_mmu, address, &type, &cb, &domain, &ap); + if (type == -1) + return ERROR_OK; + /* cacheable & bufferable means write-back region */ + if (cb == 3) + armv4_5_mmu_write_physical(target, &arm920t->armv4_5_mmu, pa, size, count, buffer); + } + + if (arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled) + { + DEBUG("I-Cache enabled, invalidating affected I-Cache line"); + arm920t_write_cp15_interpreted(target, 0xee070f35, 0x0, address); + } + } + + return retval; +} + +int arm920t_soft_reset_halt(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + if (target->state == TARGET_RUNNING) + { + target->type->halt(target); + } + + while (buf_get_u32(dbg_stat->value, EICE_DBG_CONTROL_DBGACK, 1) == 0) + { + embeddedice_read_reg(dbg_stat); + jtag_execute_queue(); + } + + target->state = TARGET_HALTED; + + /* SVC, ARM state, IRQ and FIQ disabled */ + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8, 0xd3); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + /* start fetching from 0x0 */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, 0x0); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + + armv4_5->core_mode = ARMV4_5_MODE_SVC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + arm920t_disable_mmu_caches(target, 1, 1, 1); + arm920t->armv4_5_mmu.mmu_enabled = 0; + arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = 0; + arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 0; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; +} + +int arm920t_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + arm9tdmi_init_target(cmd_ctx, target); + + return ERROR_OK; + +} + +int arm920t_quit() +{ + + return ERROR_OK; +} + +int arm920t_init_arch_info(target_t *target, arm920t_common_t *arm920t, int chain_pos, char *variant) +{ + arm9tdmi_common_t *arm9tdmi = &arm920t->arm9tdmi_common; + arm7_9_common_t *arm7_9 = &arm9tdmi->arm7_9_common; + + arm9tdmi_init_arch_info(target, arm9tdmi, chain_pos, variant); + + arm9tdmi->arch_info = arm920t; + arm920t->common_magic = ARM920T_COMMON_MAGIC; + + arm7_9->post_debug_entry = arm920t_post_debug_entry; + arm7_9->pre_restore_context = arm920t_pre_restore_context; + + arm920t->armv4_5_mmu.armv4_5_cache.ctype = -1; + arm920t->armv4_5_mmu.get_ttb = arm920t_get_ttb; + arm920t->armv4_5_mmu.read_memory = arm7_9_read_memory; + arm920t->armv4_5_mmu.write_memory = arm7_9_write_memory; + arm920t->armv4_5_mmu.disable_mmu_caches = arm920t_disable_mmu_caches; + arm920t->armv4_5_mmu.enable_mmu_caches = arm920t_enable_mmu_caches; + arm920t->armv4_5_mmu.has_tiny_pages = 1; + arm920t->armv4_5_mmu.mmu_enabled = 0; + + arm9tdmi->has_single_step = 1; + + return ERROR_OK; +} + +int arm920t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm920t_common_t *arm920t = malloc(sizeof(arm920t_common_t)); + + if (argc < 4) + { + ERROR("'target arm920t' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[3], NULL, 0); + + if (argc >= 5) + variant = strdup(args[4]); + + DEBUG("chain_pos: %i, variant: %s", chain_pos, variant); + + arm920t_init_arch_info(target, arm920t, chain_pos, variant); + + return ERROR_OK; +} + +int arm920t_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + command_t *arm920t_cmd; + + + retval = arm9tdmi_register_commands(cmd_ctx); + + arm920t_cmd = register_command(cmd_ctx, NULL, "arm920t", NULL, COMMAND_ANY, "arm920t specific commands"); + + register_command(cmd_ctx, arm920t_cmd, "cp15", arm920t_handle_cp15_command, COMMAND_EXEC, "display/modify cp15 register [value]"); + register_command(cmd_ctx, arm920t_cmd, "cp15i", arm920t_handle_cp15i_command, COMMAND_EXEC, "display/modify cp15 (interpreted access) [value] [address]"); + register_command(cmd_ctx, arm920t_cmd, "cache_info", arm920t_handle_cache_info_command, COMMAND_EXEC, "display information about target caches"); + register_command(cmd_ctx, arm920t_cmd, "virt2phys", arm920t_handle_virt2phys_command, COMMAND_EXEC, "translate va to pa "); + + register_command(cmd_ctx, arm920t_cmd, "mdw_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory words [count]"); + register_command(cmd_ctx, arm920t_cmd, "mdh_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory half-words [count]"); + register_command(cmd_ctx, arm920t_cmd, "mdb_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory bytes [count]"); + + register_command(cmd_ctx, arm920t_cmd, "mww_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory word "); + register_command(cmd_ctx, arm920t_cmd, "mwh_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory half-word "); + register_command(cmd_ctx, arm920t_cmd, "mwb_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory byte "); + + return ERROR_OK; +} + +int arm920t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + /* one or more argument, access a single register (write if second argument is given */ + if (argc >= 1) + { + int address = strtoul(args[0], NULL, 0); + + if (argc == 1) + { + u32 value; + if ((retval = arm920t_read_cp15_physical(target, address, &value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access reg %i", address); + return ERROR_OK; + } + jtag_execute_queue(); + + command_print(cmd_ctx, "%i: %8.8x", address, value); + } + else if (argc == 2) + { + u32 value = strtoul(args[1], NULL, 0); + if ((retval = arm920t_write_cp15_physical(target, address, value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access reg %i", address); + return ERROR_OK; + } + command_print(cmd_ctx, "%i: %8.8x", address, value); + } + } + + return ERROR_OK; +} + +int arm920t_handle_cp15i_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + /* one or more argument, access a single register (write if second argument is given */ + if (argc >= 1) + { + u32 opcode = strtoul(args[0], NULL, 0); + + if (argc == 1) + { + u32 value; + if ((retval = arm920t_read_cp15_interpreted(target, opcode, &value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't execute %8.8x", opcode); + return ERROR_OK; + } + + command_print(cmd_ctx, "%8.8x: %8.8x", opcode, value); + } + else if (argc == 2) + { + u32 value = strtoul(args[1], NULL, 0); + if ((retval = arm920t_write_cp15_interpreted(target, opcode, value, 0)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't execute %8.8x", opcode); + return ERROR_OK; + } + command_print(cmd_ctx, "%8.8x: %8.8x", opcode, value); + } + else if (argc == 3) + { + u32 value = strtoul(args[1], NULL, 0); + u32 address = strtoul(args[2], NULL, 0); + if ((retval = arm920t_write_cp15_interpreted(target, opcode, value, address)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't execute %8.8x", opcode); + return ERROR_OK; + } + command_print(cmd_ctx, "%8.8x: %8.8x %8.8x", opcode, value, address); + } + } + + return ERROR_OK; +} + +int arm920t_handle_cache_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + return armv4_5_handle_cache_info_command(cmd_ctx, &arm920t->armv4_5_mmu.armv4_5_cache); +} + +int arm920t_handle_virt2phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_virt2phys_command(cmd_ctx, cmd, args, argc, target, &arm920t->armv4_5_mmu); +} + +int arm920t_handle_md_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_md_phys_command(cmd_ctx, cmd, args, argc, target, &arm920t->armv4_5_mmu); +} + +int arm920t_handle_mw_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_mw_phys_command(cmd_ctx, cmd, args, argc, target, &arm920t->armv4_5_mmu); +} diff --git a/src/target/arm920t.h b/src/target/arm920t.h new file mode 100644 index 00000000..bedf31f2 --- /dev/null +++ b/src/target/arm920t.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARM920T_H +#define ARM920T_H + +#include "target.h" +#include "register.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm9tdmi.h" +#include "armv4_5_mmu.h" +#include "armv4_5_cache.h" + +#define ARM920T_COMMON_MAGIC 0xa920a920 + +typedef struct arm920t_common_s +{ + int common_magic; + armv4_5_mmu_common_t armv4_5_mmu; + arm9tdmi_common_t arm9tdmi_common; + u32 cp15_control_reg; + u32 d_fsr; + u32 i_fsr; + u32 d_far; + u32 i_far; +} arm920t_common_t; + +#endif /* ARM920T_H */ diff --git a/src/target/arm9tdmi.c b/src/target/arm9tdmi.c new file mode 100644 index 00000000..48b201a1 --- /dev/null +++ b/src/target/arm9tdmi.c @@ -0,0 +1,848 @@ +/*************************************************************************** + * 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 "arm9tdmi.h" + +#include "arm7_9_common.h" +#include "register.h" +#include "target.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "log.h" +#include "jtag.h" +#include "arm_jtag.h" + +#include +#include + +#if 0 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm9tdmi_register_commands(struct command_context_s *cmd_ctx); + +/* forward declarations */ +int arm9tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm9tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm9tdmi_quit(); + +/* target function declarations */ +enum target_state arm9tdmi_poll(struct target_s *target); +int arm9tdmi_halt(target_t *target); +int arm9tdmi_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + +target_type_t arm9tdmi_target = +{ + .name = "arm9tdmi", + + .poll = arm7_9_poll, + .arch_state = armv4_5_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm7_9_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm7_9_read_memory, + .write_memory = arm7_9_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm9tdmi_register_commands, + .target_command = arm9tdmi_target_command, + .init_target = arm9tdmi_init_target, + .quit = arm9tdmi_quit +}; + +int arm9tdmi_examine_debug_reason(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* only check the debug reason if we don't know it already */ + if ((target->debug_reason != DBG_REASON_DBGRQ) + && (target->debug_reason != DBG_REASON_SINGLESTEP)) + { + scan_field_t fields[3]; + u8 databus[4]; + u8 instructionbus[4]; + u8 debug_reason; + + jtag_add_end_state(TAP_PD); + + fields[0].device = arm7_9->jtag_info.chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = databus; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = arm7_9->jtag_info.chain_pos; + fields[1].num_bits = 3; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = &debug_reason; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = arm7_9->jtag_info.chain_pos; + fields[2].num_bits = 32; + fields[2].out_value = NULL; + fields[2].out_mask = NULL; + fields[2].in_value = instructionbus; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + arm_jtag_scann(&arm7_9->jtag_info, 0x1); + arm_jtag_set_instr(&arm7_9->jtag_info, arm7_9->jtag_info.intest_instr); + + jtag_add_dr_scan(3, fields, TAP_PD); + jtag_execute_queue(); + + fields[0].in_value = NULL; + fields[0].out_value = databus; + fields[1].in_value = NULL; + fields[1].out_value = &debug_reason; + fields[2].in_value = NULL; + fields[2].out_value = instructionbus; + + jtag_add_dr_scan(3, fields, TAP_PD); + + if (debug_reason & 0x4) + if (debug_reason & 0x2) + target->debug_reason = DBG_REASON_WPTANDBKPT; + else + target->debug_reason = DBG_REASON_WATCHPOINT; + else + target->debug_reason = DBG_REASON_BREAKPOINT; + } + + return ERROR_OK; +} + +/* put an instruction in the ARM9TDMI pipeline or write the data bus, and optionally read data */ +int arm9tdmi_clock_out(arm_jtag_t *jtag_info, u32 instr, u32 out, u32 *in, int sysspeed) +{ + scan_field_t fields[3]; + u8 out_buf[4]; + u8 instr_buf[4]; + u8 sysspeed_buf = 0x0; + + /* prepare buffer */ + buf_set_u32(out_buf, 0, 32, out); + + instr = flip_u32(instr, 32); + buf_set_u32(instr_buf, 0, 32, instr); + + if (sysspeed) + buf_set_u32(&sysspeed_buf, 2, 1, 1); + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = out_buf; + fields[0].out_mask = NULL; + if (in) + { + fields[0].in_value = (u8*)in; + } else + { + fields[0].in_value = NULL; + } + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 3; + fields[1].out_value = &sysspeed_buf; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 32; + fields[2].out_value = instr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ + { + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("instr: 0x%8.8x, out: 0x%8.8x, in: %s", flip_u32(instr, 32), out, in_string); + free(in_string); + } + else + DEBUG("instr: 0x%8.8x, out: 0x%8.8x", flip_u32(instr, 32), out); + } +#endif + + return ERROR_OK; +} + +/* just read data (instruction and data-out = don't care) */ +int arm9tdmi_clock_data_in(arm_jtag_t *jtag_info, u32 *in) +{ + scan_field_t fields[3]; + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = (u8*)in; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 3; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 32; + fields[2].out_value = NULL; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ + { + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("in: %s", in_string); + free(in_string); + } + } +#endif + + return ERROR_OK; +} + +void arm9tdmi_change_to_arm(target_t *target, u32 *r0, u32 *pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* save r0 before using it and put system in ARM state + * to allow common handling of ARM and THUMB debugging */ + + /* fetch STR r0, [r0] */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* STR r0, [r0] in Memory */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, r0, 0); + + /* MOV r0, r15 fetched, STR in Decode */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_MOV(0, 15), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* nothing fetched, STR r0, [r0] in Memory */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, pc, 0); + + /* fetch MOV */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + /* fetch BX */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_BX(0), 0, NULL, 0); + /* NOP fetched, BX in Decode, MOV in Execute */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* NOP fetched, BX in Execute (1) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + jtag_execute_queue(); + + /* fix program counter: + * MOV r0, r15 was the 5th instruction (+8) + * reading PC in Thumb state gives address of instruction + 4 + */ + *pc -= 0xc; +} + +void arm9tdmi_read_core_regs(target_t *target, u32 mask, u32* core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* STMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), 0, NULL, 0); + + /* fetch NOP, STM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, STM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, STM in MEMORY (i'th cycle) */ + arm9tdmi_clock_data_in(jtag_info, core_regs[i]); + } + +} + +void arm9tdmi_read_xpsr(target_t *target, u32 *xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* MRS r0, cpsr */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MRS(0, spsr & 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* STR r0, [r15] */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 15), 0, NULL, 0); + /* fetch NOP, STR in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, STR in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, STR in MEMORY */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, xpsr, 0); + +} + +void arm9tdmi_write_xpsr(target_t *target, u32 xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr: %8.8x, spsr: %i", xpsr, spsr); + + /* MSR1 fetched */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr & 0xff, 0, 1, spsr), 0, NULL, 0); + /* MSR2 fetched, MSR1 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff00) >> 8, 0xc, 2, spsr), 0, NULL, 0); + /* MSR3 fetched, MSR1 in EXECUTE (1), MSR2 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff0000) >> 16, 0x8, 4, spsr), 0, NULL, 0); + /* nothing fetched, MSR1 in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR1 in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* MSR4 fetched, MSR2 in EXECUTE (1), MSR3 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff000000) >> 24, 0x4, 8, spsr), 0, NULL, 0); + /* nothing fetched, MSR2 in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR2 in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* NOP fetched, MSR3 in EXECUTE (1), MSR4 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR3 in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR3 in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* NOP fetched, MSR4 in EXECUTE (1) */ + /* last MSR writes flags, which takes only one cycle */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); +} + +void arm9tdmi_write_xpsr_im8(target_t *target, u8 xpsr_im, int rot, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr_im: %2.2x, rot: %i, spsr: %i", xpsr_im, rot, spsr); + + /* MSR fetched */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr_im, rot, 1, spsr), 0, NULL, 0); + /* NOP fetched, MSR in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* NOP fetched, MSR in EXECUTE (1) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* rot == 4 writes flags, which takes only one cycle */ + if (rot != 4) + { + /* nothing fetched, MSR in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + } +} + +void arm9tdmi_write_core_regs(target_t *target, u32 mask, u32 core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 0), 0, NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, LDM still in EXECUTE (1+i cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, core_regs[i], NULL, 0); + } + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + +} + +void arm9tdmi_load_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load-multiple into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_load_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load half-word into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDRH_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); +} + +void arm9tdmi_load_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load byte into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDRB_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_store_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store-multiple into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask, 0, 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_store_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store half-word into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STRH_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_store_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store byte into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STRB_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_write_pc(target_t *target, u32 pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x8000, 0, 0), 0, NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) (output data) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, pc, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (4th cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (5th cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + +} + +void arm9tdmi_branch_resume(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + arm9tdmi_clock_out(jtag_info, ARMV4_5_B(0xfffffc, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_branch_resume_thumb(target_t *target) +{ + DEBUG(""); + + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x1, 0, 0), 0, NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) | 1, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* Branch and eXchange */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_BX(0), 0, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* target is now in Thumb state */ + embeddedice_read_reg(dbg_stat); + + /* clean r0 bits to avoid alignment problems */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), 0, NULL, 0); + /* load r0 value, MOV_IM in Decode*/ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_LDR(0, 0), 0, NULL, 0); + /* fetch NOP, LDR in Decode, MOV_IM in Execute */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* fetch NOP, LDR in Execute */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (2nd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32), NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (3rd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_B(0x7f6), 0, NULL, 1); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + +} + +void arm9tdmi_enable_single_step(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9 = arm7_9->arch_info; + + if (arm9->has_single_step) + { + buf_set_u32(arm7_9->eice_cache->reg_list[EICE_DBG_CTRL].value, 3, 1, 1); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]); + } + else + { + arm7_9_enable_eice_step(target); + } +} + +void arm9tdmi_disable_single_step(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9 = arm7_9->arch_info; + + if (arm9->has_single_step) + { + buf_set_u32(arm7_9->eice_cache->reg_list[EICE_DBG_CTRL].value, 3, 1, 0); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]); + } + else + { + arm7_9_disable_eice_step(target); + } +} + +void arm9tdmi_build_reg_cache(target_t *target) +{ + reg_cache_t **cache_p = register_get_last_cache_p(&target->reg_cache); + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + + + (*cache_p) = armv4_5_build_reg_cache(target, armv4_5); + armv4_5->core_cache = (*cache_p); + + (*cache_p)->next = embeddedice_build_reg_cache(target, jtag_info, 0); + arm7_9->eice_cache = (*cache_p)->next; + + if (arm9tdmi->has_monitor_mode) + (*cache_p)->next->reg_list[0].size = 6; + else + (*cache_p)->next->reg_list[0].size = 4; + + (*cache_p)->next->reg_list[1].size = 5; + +} + +int arm9tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + + arm9tdmi_build_reg_cache(target); + + return ERROR_OK; + +} + +int arm9tdmi_quit() +{ + + return ERROR_OK; +} + +int arm9tdmi_init_arch_info(target_t *target, arm9tdmi_common_t *arm9tdmi, int chain_pos, char *variant) +{ + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + arm7_9 = &arm9tdmi->arm7_9_common; + armv4_5 = &arm7_9->armv4_5_common; + + /* prepare JTAG information for the new target */ + arm7_9->jtag_info.chain_pos = chain_pos; + arm7_9->jtag_info.scann_size = 5; + + /* register arch-specific functions */ + arm7_9->examine_debug_reason = arm9tdmi_examine_debug_reason; + arm7_9->change_to_arm = arm9tdmi_change_to_arm; + arm7_9->read_core_regs = arm9tdmi_read_core_regs; + arm7_9->read_xpsr = arm9tdmi_read_xpsr; + + arm7_9->write_xpsr = arm9tdmi_write_xpsr; + arm7_9->write_xpsr_im8 = arm9tdmi_write_xpsr_im8; + arm7_9->write_core_regs = arm9tdmi_write_core_regs; + + arm7_9->load_word_regs = arm9tdmi_load_word_regs; + arm7_9->load_hword_reg = arm9tdmi_load_hword_reg; + arm7_9->load_byte_reg = arm9tdmi_load_byte_reg; + + arm7_9->store_word_regs = arm9tdmi_store_word_regs; + arm7_9->store_hword_reg = arm9tdmi_store_hword_reg; + arm7_9->store_byte_reg = arm9tdmi_store_byte_reg; + + arm7_9->write_pc = arm9tdmi_write_pc; + arm7_9->branch_resume = arm9tdmi_branch_resume; + arm7_9->branch_resume_thumb = arm9tdmi_branch_resume_thumb; + + arm7_9->enable_single_step = arm9tdmi_enable_single_step; + arm7_9->disable_single_step = arm9tdmi_disable_single_step; + + arm7_9->pre_debug_entry = NULL; + arm7_9->post_debug_entry = NULL; + + arm7_9->pre_restore_context = NULL; + arm7_9->post_restore_context = NULL; + + /* initialize arch-specific breakpoint handling */ + buf_set_u32((u8*)(&arm7_9->arm_bkpt), 0, 32, 0xdeeedeee); + buf_set_u32((u8*)(&arm7_9->thumb_bkpt), 0, 16, 0xdeee); + + arm7_9->sw_bkpts_use_wp = 1; + arm7_9->sw_bkpts_enabled = 0; + arm7_9->dbgreq_adjust_pc = 3; + arm7_9->arch_info = arm9tdmi; + arm7_9->use_dbgrq = 1; + + arm9tdmi->common_magic = ARM9TDMI_COMMON_MAGIC; + arm9tdmi->has_monitor_mode = 0; + arm9tdmi->has_single_step = 0; + arm9tdmi->arch_info = NULL; + + if (variant) + { + if (strcmp(variant, "arm920t") == 0) + arm9tdmi->has_single_step = 1; + else if (strcmp(variant, "arm922t") == 0) + arm9tdmi->has_single_step = 1; + else if (strcmp(variant, "arm940t") == 0) + arm9tdmi->has_single_step = 1; + } + + arm7_9_init_arch_info(target, arm7_9); + + return ERROR_OK; +} + +/* target arm9tdmi */ +int arm9tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm9tdmi_common_t *arm9tdmi = malloc(sizeof(arm9tdmi_common_t)); + + if (argc < 4) + { + ERROR("'target arm9tdmi' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[3], NULL, 0); + + if (argc >= 5) + variant = strdup(args[4]); + + arm9tdmi_init_arch_info(target, arm9tdmi, chain_pos, variant); + + return ERROR_OK; +} + +int arm9tdmi_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + + retval = arm7_9_register_commands(cmd_ctx); + + return ERROR_OK; + +} + diff --git a/src/target/arm9tdmi.h b/src/target/arm9tdmi.h new file mode 100644 index 00000000..7bbaac7c --- /dev/null +++ b/src/target/arm9tdmi.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARM9TDMI_H +#define ARM9TDMI_H + +#include "target.h" +#include "register.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm7_9_common.h" + +#define ARM9TDMI_COMMON_MAGIC 0x00a900a9 + +typedef struct arm9tdmi_common_s +{ + int common_magic; + char *variant; + int has_monitor_mode; + int has_single_step; + void *arch_info; + arm7_9_common_t arm7_9_common; +} arm9tdmi_common_t; + +extern int arm9tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +extern int arm9tdmi_init_arch_info(target_t *target, arm9tdmi_common_t *arm9tdmi, int chain_pos, char *variant); +extern int arm9tdmi_register_commands(struct command_context_s *cmd_ctx); + +extern int arm9tdmi_clock_out(arm_jtag_t *jtag_info, u32 instr, u32 out, u32 *in, int sysspeed); +extern int arm9tdmi_clock_data_in(arm_jtag_t *jtag_info, u32 *in); +extern void arm9tdmi_read_core_regs(target_t *target, u32 mask, u32* core_regs[16]); +extern void arm9tdmi_write_core_regs(target_t *target, u32 mask, u32 core_regs[16]); + +#endif /* ARM9TDMI_H */ diff --git a/src/target/arm_jtag.c b/src/target/arm_jtag.c new file mode 100644 index 00000000..c401d8f3 --- /dev/null +++ b/src/target/arm_jtag.c @@ -0,0 +1,116 @@ +/*************************************************************************** + * 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 "arm_jtag.h" + +#include "binarybuffer.h" +#include "log.h" +#include "jtag.h" + +#include + +int arm_jtag_set_instr(arm_jtag_t *jtag_info, u32 new_instr) +{ + jtag_device_t *device = jtag_get_device(jtag_info->chain_pos); + + if (buf_get_u32(device->cur_instr, 0, device->ir_length) != new_instr) + { + scan_field_t field; + + field.device = jtag_info->chain_pos; + field.num_bits = device->ir_length; + field.out_value = calloc(CEIL(field.num_bits, 8), 1); + buf_set_u32(field.out_value, 0, field.num_bits, new_instr); + field.out_mask = NULL; + field.in_value = NULL; + field.in_check_value = NULL; + field.in_check_mask = NULL; + field.in_handler = NULL; + field.in_handler_priv = NULL; + + jtag_add_ir_scan(1, &field, -1); + + free(field.out_value); + } + + return ERROR_OK; +} + +int arm_jtag_scann(arm_jtag_t *jtag_info, u32 new_scan_chain) +{ + if(jtag_info->cur_scan_chain != new_scan_chain) + { + scan_field_t field; + + field.device = jtag_info->chain_pos; + field.num_bits = jtag_info->scann_size; + field.out_value = calloc(CEIL(field.num_bits, 8), 1); + buf_set_u32(field.out_value, 0, field.num_bits, new_scan_chain); + field.out_mask = NULL; + //field.in_value = &scan_n_capture; + field.in_value = NULL; + field.in_check_value = NULL; + field.in_check_mask = NULL; + field.in_handler = NULL; + field.in_handler_priv = NULL; + + arm_jtag_set_instr(jtag_info, jtag_info->scann_instr); + jtag_add_dr_scan(1, &field, -1); + + jtag_info->cur_scan_chain = new_scan_chain; + + free(field.out_value); + } + + return ERROR_OK; +} + +int arm_jtag_reset_callback(enum jtag_event event, void *priv) +{ + arm_jtag_t *jtag_info = priv; + + if (event == JTAG_TRST_ASSERTED) + { + jtag_info->cur_scan_chain = 0; + } + + return ERROR_OK; +} + +int arm_jtag_setup_connection(arm_jtag_t *jtag_info) +{ + jtag_info->scann_instr = 0x2; + jtag_info->cur_scan_chain = 0; + jtag_info->intest_instr = 0xc; + + jtag_register_event_callback(arm_jtag_reset_callback, jtag_info); + + return ERROR_OK; +} + +int arm_jtag_buf_to_u32_flip(u8 *in_buf, void *priv) +{ + u32 *dest = priv; + + *dest = flip_u32(buf_get_u32(in_buf, 0, 32), 32); + + return ERROR_OK; +} diff --git a/src/target/arm_jtag.h b/src/target/arm_jtag.h new file mode 100644 index 00000000..a9f90846 --- /dev/null +++ b/src/target/arm_jtag.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARM_JTAG +#define ARM_JTAG + +#include "types.h" + +typedef struct arm_jtag_s +{ + int chain_pos; + + int scann_size; + u32 scann_instr; + int cur_scan_chain; + + u32 intest_instr; +} arm_jtag_t; + +extern int arm_jtag_set_instr(arm_jtag_t *jtag_info, u32 new_instr); +extern int arm_jtag_scann(arm_jtag_t *jtag_info, u32 new_scan_chain); +extern int arm_jtag_buf_to_u32_flip(u8 *in_buf, void *priv); +extern int arm_jtag_setup_connection(arm_jtag_t *jtag_info); + +#endif /* ARM_JTAG */ + diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c new file mode 100644 index 00000000..51fd4b42 --- /dev/null +++ b/src/target/armv4_5.c @@ -0,0 +1,583 @@ +/*************************************************************************** + * 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 "armv4_5.h" + +#include "target.h" +#include "register.h" +#include "log.h" +#include "binarybuffer.h" +#include "command.h" + +#include +#include +#include + +bitfield_desc_t armv4_5_psr_bitfield_desc[] = +{ + {"M[4:0]", 5}, + {"T", 1}, + {"F", 1}, + {"I", 1}, + {"reserved", 16}, + {"J", 1}, + {"reserved", 2}, + {"Q", 1}, + {"V", 1}, + {"C", 1}, + {"Z", 1}, + {"N", 1}, +}; + +char* armv4_5_core_reg_list[] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13_usr", "lr_usr", "pc", + + "r8_fiq", "r9_fiq", "r10_fiq", "r11_fiq", "r12_fiq", "r13_fiq", "lr_fiq", + + "r13_irq", "lr_irq", + + "r13_svc", "lr_svc", + + "r13_abt", "lr_abt", + + "r13_und", "lr_und", + + "cpsr", "spsr_fiq", "spsr_irq", "spsr_svc", "spsr_abt", "spsr_und" +}; + +char* armv4_5_mode_strings[] = +{ + "User", "FIQ", "IRQ", "Supervisor", "Abort", "Undefined", "System" +}; + +char* armv4_5_state_strings[] = +{ + "ARM", "Thumb", "Jazelle" +}; + +int armv4_5_core_reg_arch_type = -1; + +armv4_5_core_reg_t armv4_5_core_reg_list_arch_info[] = +{ + {0, ARMV4_5_MODE_ANY, NULL, NULL}, + {1, ARMV4_5_MODE_ANY, NULL, NULL}, + {2, ARMV4_5_MODE_ANY, NULL, NULL}, + {3, ARMV4_5_MODE_ANY, NULL, NULL}, + {4, ARMV4_5_MODE_ANY, NULL, NULL}, + {5, ARMV4_5_MODE_ANY, NULL, NULL}, + {6, ARMV4_5_MODE_ANY, NULL, NULL}, + {7, ARMV4_5_MODE_ANY, NULL, NULL}, + {8, ARMV4_5_MODE_ANY, NULL, NULL}, + {9, ARMV4_5_MODE_ANY, NULL, NULL}, + {10, ARMV4_5_MODE_ANY, NULL, NULL}, + {11, ARMV4_5_MODE_ANY, NULL, NULL}, + {12, ARMV4_5_MODE_ANY, NULL, NULL}, + {13, ARMV4_5_MODE_USR, NULL, NULL}, + {14, ARMV4_5_MODE_USR, NULL, NULL}, + {15, ARMV4_5_MODE_ANY, NULL, NULL}, + + {8, ARMV4_5_MODE_FIQ, NULL, NULL}, + {9, ARMV4_5_MODE_FIQ, NULL, NULL}, + {10, ARMV4_5_MODE_FIQ, NULL, NULL}, + {11, ARMV4_5_MODE_FIQ, NULL, NULL}, + {12, ARMV4_5_MODE_FIQ, NULL, NULL}, + {13, ARMV4_5_MODE_FIQ, NULL, NULL}, + {14, ARMV4_5_MODE_FIQ, NULL, NULL}, + + {13, ARMV4_5_MODE_IRQ, NULL, NULL}, + {14, ARMV4_5_MODE_IRQ, NULL, NULL}, + + {13, ARMV4_5_MODE_SVC, NULL, NULL}, + {14, ARMV4_5_MODE_SVC, NULL, NULL}, + + {13, ARMV4_5_MODE_ABT, NULL, NULL}, + {14, ARMV4_5_MODE_ABT, NULL, NULL}, + + {13, ARMV4_5_MODE_UND, NULL, NULL}, + {14, ARMV4_5_MODE_UND, NULL, NULL}, + + {16, ARMV4_5_MODE_ANY, NULL, NULL}, + {16, ARMV4_5_MODE_FIQ, NULL, NULL}, + {16, ARMV4_5_MODE_IRQ, NULL, NULL}, + {16, ARMV4_5_MODE_SVC, NULL, NULL}, + {16, ARMV4_5_MODE_ABT, NULL, NULL}, + {16, ARMV4_5_MODE_UND, NULL, NULL} +}; + +/* map core mode (USR, FIQ, ...) and register number to indizes into the register cache */ +int armv4_5_core_reg_map[7][17] = +{ + { /* USR */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 31 + }, + { /* FIQ */ + 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 15, 32 + }, + { /* IRQ */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 23, 24, 15, 33 + }, + { /* SVC */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 26, 15, 34 + }, + { /* ABT */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 27, 28, 15, 35 + }, + { /* UND */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 29, 30, 15, 36 + }, + { /* SYS */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 31 + } +}; + +u8 armv4_5_gdb_dummy_fp_value[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +reg_t armv4_5_gdb_dummy_fp_reg = +{ + "GDB dummy floating-point register", armv4_5_gdb_dummy_fp_value, 0, 1, 96, NULL, 0, NULL, 0 +}; + +u8 armv4_5_gdb_dummy_fps_value[] = {0, 0, 0, 0}; + +reg_t armv4_5_gdb_dummy_fps_reg = +{ + "GDB dummy floating-point status register", armv4_5_gdb_dummy_fps_value, 0, 1, 32, NULL, 0, NULL, 0 +}; + +/* map psr mode bits to linear number */ +int armv4_5_mode_to_number(enum armv4_5_mode mode) +{ + switch (mode) + { + case 16: return 0; break; + case 17: return 1; break; + case 18: return 2; break; + case 19: return 3; break; + case 23: return 4; break; + case 27: return 5; break; + case 31: return 6; break; + case -1: return 0; break; /* map MODE_ANY to user mode */ + default: + ERROR("invalid mode value encountered"); + return -1; + } +} + +/* map linear number to mode bits */ +enum armv4_5_mode armv4_5_number_to_mode(int number) +{ + switch(number) + { + case 0: return ARMV4_5_MODE_USR; break; + case 1: return ARMV4_5_MODE_FIQ; break; + case 2: return ARMV4_5_MODE_IRQ; break; + case 3: return ARMV4_5_MODE_SVC; break; + case 4: return ARMV4_5_MODE_ABT; break; + case 5: return ARMV4_5_MODE_UND; break; + case 6: return ARMV4_5_MODE_SYS; break; + default: + ERROR("mode index out of bounds"); + return -1; + } +}; + +int armv4_5_get_core_reg(reg_t *reg) +{ + int retval; + armv4_5_core_reg_t *armv4_5 = reg->arch_info; + target_t *target = armv4_5->target; + + if (target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + //retval = armv4_5->armv4_5_common->full_context(target); + retval = armv4_5->armv4_5_common->read_core_reg(target, armv4_5->num, armv4_5->mode); + + return retval; +} + +int armv4_5_set_core_reg(reg_t *reg, u32 value) +{ + armv4_5_core_reg_t *armv4_5 = reg->arch_info; + target_t *target = armv4_5->target; + + if (target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + buf_set_u32(reg->value, 0, 32, value); + reg->dirty = 1; + reg->valid = 1; + + return ERROR_OK; +} + +int armv4_5_invalidate_core_regs(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + int i; + + for (i = 0; i < 37; i++) + { + armv4_5->core_cache->reg_list[i].valid = 0; + armv4_5->core_cache->reg_list[i].dirty = 0; + } + + return ERROR_OK; +} + +reg_cache_t* armv4_5_build_reg_cache(target_t *target, armv4_5_common_t *armv4_5_common) +{ + int num_regs = 37; + reg_cache_t *cache = malloc(sizeof(reg_cache_t)); + reg_t *reg_list = malloc(sizeof(reg_t) * num_regs); + armv4_5_core_reg_t *arch_info = malloc(sizeof(reg_t) * num_regs); + int i; + + cache->name = "arm v4/5 registers"; + cache->next = NULL; + cache->reg_list = reg_list; + cache->num_regs = num_regs; + + if (armv4_5_core_reg_arch_type == -1) + armv4_5_core_reg_arch_type = register_reg_arch_type(armv4_5_get_core_reg, armv4_5_set_core_reg); + + for (i = 0; i < 37; i++) + { + arch_info[i] = armv4_5_core_reg_list_arch_info[i]; + arch_info[i].target = target; + arch_info[i].armv4_5_common = armv4_5_common; + reg_list[i].name = armv4_5_core_reg_list[i]; + reg_list[i].size = 32; + reg_list[i].value = calloc(1, 4); + reg_list[i].dirty = 0; + reg_list[i].valid = 0; + reg_list[i].bitfield_desc = NULL; + reg_list[i].num_bitfields = 0; + reg_list[i].arch_type = armv4_5_core_reg_arch_type; + reg_list[i].arch_info = &arch_info[i]; + } + + return cache; +} + +int armv4_5_arch_state(struct target_s *target, char *buf, int buf_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("BUG: called for a non-ARMv4/5 target"); + exit(-1); + } + + snprintf(buf, buf_size, + "target halted in %s state due to %s, current mode: %s\ncpsr: 0x%8.8x pc: 0x%8.8x", + armv4_5_state_strings[armv4_5->core_state], + target_debug_reason_strings[target->debug_reason], + armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)], + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + + return ERROR_OK; +} + +int handle_armv4_5_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + char output[128]; + int output_len; + int mode, num; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5 = target->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + command_print(cmd_ctx, "current target isn't an ARMV4/5 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "error: target must be halted for register accesses"); + return ERROR_OK; + } + + for (num = 0; num <= 15; num++) + { + output_len = 0; + for (mode = 0; mode < 6; mode++) + { + if (!ARMV4_5_CORE_REG_MODENUM(armv4_5->core_cache, mode, num).valid) + { + armv4_5->full_context(target); + } + output_len += snprintf(output + output_len, 128 - output_len, "%8s: %8.8x ", ARMV4_5_CORE_REG_MODENUM(armv4_5->core_cache, mode, num).name, + buf_get_u32(ARMV4_5_CORE_REG_MODENUM(armv4_5->core_cache, mode, num).value, 0, 32)); + } + command_print(cmd_ctx, output); + } + command_print(cmd_ctx, " cpsr: %8.8x spsr_fiq: %8.8x spsr_irq: %8.8x spsr_svc: %8.8x spsr_abt: %8.8x spsr_und: %8.8x", + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_FIQ].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_IRQ].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_SVC].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_ABT].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_UND].value, 0, 32)); + + return ERROR_OK; +} + +int handle_armv4_5_core_state_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5 = target->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + command_print(cmd_ctx, "current target isn't an ARMV4/5 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp(args[0], "arm") == 0) + { + armv4_5->core_state = ARMV4_5_STATE_ARM; + } + if (strcmp(args[0], "thumb") == 0) + { + armv4_5->core_state = ARMV4_5_STATE_THUMB; + } + } + + command_print(cmd_ctx, "core state: %s", armv4_5_state_strings[armv4_5->core_state]); + + return ERROR_OK; +} + +int armv4_5_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *armv4_5_cmd; + + armv4_5_cmd = register_command(cmd_ctx, NULL, "armv4_5", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, armv4_5_cmd, "reg", handle_armv4_5_reg_command, COMMAND_EXEC, "display ARM core registers"); + register_command(cmd_ctx, armv4_5_cmd, "core_state", handle_armv4_5_core_state_command, COMMAND_EXEC, "display/change ARM core state "); + + return ERROR_OK; +} + +int armv4_5_get_gdb_reg_list(target_t *target, reg_t **reg_list[], int *reg_list_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + int i; + + if (target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + *reg_list_size = 26; + *reg_list = malloc(sizeof(reg_t*) * (*reg_list_size)); + + for (i = 0; i < 16; i++) + { + (*reg_list)[i] = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i); + } + + for (i = 16; i < 24; i++) + { + (*reg_list)[i] = &armv4_5_gdb_dummy_fp_reg; + } + + (*reg_list)[24] = &armv4_5_gdb_dummy_fps_reg; + (*reg_list)[25] = &armv4_5->core_cache->reg_list[ARMV4_5_CPSR]; + + return ERROR_OK; +} + +int armv4_5_run_algorithm(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_params, reg_param_t *reg_params, u32 entry_point, u32 exit_point, int timeout_ms, void *arch_info) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + armv4_5_algorithm_t *armv4_5_algorithm_info = arch_info; + enum armv4_5_state core_state = armv4_5->core_state; + enum armv4_5_mode core_mode = armv4_5->core_mode; + u32 context[17]; + u32 cpsr; + int exit_breakpoint_size = 0; + int i; + int retval = ERROR_OK; + + if (armv4_5_algorithm_info->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("current target isn't an ARMV4/5 target"); + return ERROR_TARGET_INVALID; + } + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + for (i = 0; i <= 16; i++) + { + if (!ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).valid) + armv4_5->read_core_reg(target, i, armv4_5_algorithm_info->core_mode); + context[i] = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).value, 0, 32); + } + cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32); + + for (i = 0; i < num_mem_params; i++) + { + target_write_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value); + } + + for (i = 0; i < num_reg_params; i++) + { + reg_t *reg = register_get_by_name(armv4_5->core_cache, reg_params[i].reg_name, 0); + if (!reg) + { + ERROR("BUG: register '%s' not found", reg_params[i].reg_name); + exit(-1); + } + + if (reg->size != reg_params[i].size) + { + ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name); + exit(-1); + } + + armv4_5_set_core_reg(reg, buf_get_u32(reg_params[i].value, 0, 32)); + } + + armv4_5->core_state = armv4_5_algorithm_info->core_state; + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + exit_breakpoint_size = 4; + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + exit_breakpoint_size = 2; + else + { + ERROR("BUG: can't execute algorithms when not in ARM or Thumb state"); + exit(-1); + } + + if (armv4_5_algorithm_info->core_mode != ARMV4_5_MODE_ANY) + { + DEBUG("setting core_mode: 0x%2.2x", armv4_5_algorithm_info->core_mode); + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 5, armv4_5_algorithm_info->core_mode); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + } + + if ((retval = breakpoint_add(target, exit_point, exit_breakpoint_size, BKPT_HARD)) != ERROR_OK) + { + ERROR("can't add breakpoint to finish algorithm execution"); + return ERROR_TARGET_FAILURE; + } + + target->type->resume(target, 0, entry_point, 1, 1); + target->type->poll(target); + + while (target->state != TARGET_HALTED) + { + usleep(10000); + target->type->poll(target); + if ((timeout_ms -= 10) <= 0) + { + ERROR("timeout waiting for algorithm to complete, trying to halt target"); + target->type->halt(target); + timeout_ms = 1000; + while (target->state != TARGET_HALTED) + { + usleep(10000); + target->type->poll(target); + if ((timeout_ms -= 10) <= 0) + { + ERROR("target didn't reenter debug state, exiting"); + exit(-1); + } + } + retval = ERROR_TARGET_TIMEOUT; + } + } + + breakpoint_remove(target, exit_point); + + for (i = 0; i < num_mem_params; i++) + { + if (mem_params[i].direction != PARAM_OUT) + target_read_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value); + } + + for (i = 0; i < num_reg_params; i++) + { + if (reg_params[i].direction != PARAM_OUT) + { + + reg_t *reg = register_get_by_name(armv4_5->core_cache, reg_params[i].reg_name, 0); + if (!reg) + { + ERROR("BUG: register '%s' not found", reg_params[i].reg_name); + exit(-1); + } + + if (reg->size != reg_params[i].size) + { + ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name); + exit(-1); + } + + buf_set_u32(reg_params[i].value, 0, 32, buf_get_u32(reg->value, 0, 32)); + } + } + + for (i = 0; i <= 16; i++) + { + DEBUG("restoring register %s with value 0x%8.8x", ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).name, buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).value, 0, 32)); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).value, 0, 32, context[i]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).dirty = 1; + } + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, cpsr); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + + armv4_5->core_state = core_state; + armv4_5->core_mode = core_mode; + + return retval; +} + +int armv4_5_init_arch_info(target_t *target, armv4_5_common_t *armv4_5) +{ + target->arch_info = armv4_5; + + armv4_5->common_magic = ARMV4_5_COMMON_MAGIC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + armv4_5->core_mode = ARMV4_5_MODE_USR; + + return ERROR_OK; +} diff --git a/src/target/armv4_5.h b/src/target/armv4_5.h new file mode 100644 index 00000000..a28bfa12 --- /dev/null +++ b/src/target/armv4_5.h @@ -0,0 +1,237 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARMV4_5_H +#define ARMV4_5_H + +#include "register.h" +#include "target.h" + +enum armv4_5_mode +{ + ARMV4_5_MODE_USR = 16, + ARMV4_5_MODE_FIQ = 17, + ARMV4_5_MODE_IRQ = 18, + ARMV4_5_MODE_SVC = 19, + ARMV4_5_MODE_ABT = 23, + ARMV4_5_MODE_UND = 27, + ARMV4_5_MODE_SYS = 31, + ARMV4_5_MODE_ANY = -1 +}; + +extern char* armv4_5_mode_strings[]; + +enum armv4_5_state +{ + ARMV4_5_STATE_ARM, + ARMV4_5_STATE_THUMB, + ARMV4_5_STATE_JAZELLE, +}; + +extern char* armv4_5_state_strings[]; + +extern int armv4_5_core_reg_map[7][17]; + +#define ARMV4_5_CORE_REG_MODE(cache, mode, num) \ + cache->reg_list[armv4_5_core_reg_map[armv4_5_mode_to_number(mode)][num]] +#define ARMV4_5_CORE_REG_MODENUM(cache, mode, num) \ + cache->reg_list[armv4_5_core_reg_map[mode][num]] + +/* offsets into armv4_5 core register cache */ +enum +{ + ARMV4_5_CPSR = 31, + ARMV4_5_SPSR_FIQ = 32, + ARMV4_5_SPSR_IRQ = 33, + ARMV4_5_SPSR_SVC = 34, + ARMV4_5_SPSR_ABT = 35, + ARMV4_5_SPSR_UND = 36 +}; + +#define ARMV4_5_COMMON_MAGIC 0x0A450A45 + +typedef struct armv4_5_common_s +{ + int common_magic; + reg_cache_t *core_cache; + enum armv4_5_mode core_mode; + enum armv4_5_state core_state; + int (*full_context)(struct target_s *target); + int (*read_core_reg)(struct target_s *target, int num, enum armv4_5_mode mode); + int (*write_core_reg)(struct target_s *target, int num, enum armv4_5_mode mode, u32 value); + void *arch_info; +} armv4_5_common_t; + +typedef struct armv4_5_algorithm_s +{ + int common_magic; + + enum armv4_5_mode core_mode; + enum armv4_5_state core_state; +} armv4_5_algorithm_t; + +typedef struct armv4_5_core_reg_s +{ + int num; + enum armv4_5_mode mode; + target_t *target; + armv4_5_common_t *armv4_5_common; +} armv4_5_core_reg_t; + +extern reg_cache_t* armv4_5_build_reg_cache(target_t *target, armv4_5_common_t *armv4_5_common); +extern enum armv4_5_mode armv4_5_number_to_mode(int number); +extern int armv4_5_mode_to_number(enum armv4_5_mode mode); + +extern int armv4_5_arch_state(struct target_s *target, char *buf, int buf_size); +extern int armv4_5_get_gdb_reg_list(target_t *target, reg_t **reg_list[], int *reg_list_size); +extern int armv4_5_invalidate_core_regs(target_t *target); + +extern int armv4_5_register_commands(struct command_context_s *cmd_ctx); +extern int armv4_5_init_arch_info(target_t *target, armv4_5_common_t *armv4_5); + +extern int armv4_5_run_algorithm(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_params, reg_param_t *reg_params, u32 entry_point, u32 exit_point, int timeout_ms, void *arch_info); + +extern int armv4_5_invalidate_core_regs(target_t *target); + +/* ARM mode instructions + */ + +/* Store multiple increment after + * Rn: base register + * List: for each bit in list: store register + * S: in priviledged mode: store user-mode registers + * W=1: update the base register. W=0: leave the base register untouched + */ +#define ARMV4_5_STMIA(Rn, List, S, W) (0xe8800000 | (S << 22) | (W << 21) | (Rn << 16) | (List)) + +/* Load multiple increment after + * Rn: base register + * List: for each bit in list: store register + * S: in priviledged mode: store user-mode registers + * W=1: update the base register. W=0: leave the base register untouched + */ +#define ARMV4_5_LDMIA(Rn, List, S, W) (0xe8900000 | (S << 22) | (W << 21) | (Rn << 16) | (List)) + +/* MOV r8, r8 */ +#define ARMV4_5_NOP (0xe1a08008) + +/* Move PSR to general purpose register + * R=1: SPSR R=0: CPSR + * Rn: target register + */ +#define ARMV4_5_MRS(Rn, R) (0xe10f0000 | (R << 22) | (Rn << 12)) + +/* Store register + * Rd: register to store + * Rn: base register + */ +#define ARMV4_5_STR(Rd, Rn) (0xe5800000 | (Rd << 12) | (Rn << 16)) + +/* Load register + * Rd: register to load + * Rn: base register + */ +#define ARMV4_5_LDR(Rd, Rn) (0xe5900000 | (Rd << 12) | (Rn << 16)) + +/* Move general purpose register to PSR + * R=1: SPSR R=0: CPSR + * Field: Field mask + * 1: control field 2: extension field 4: status field 8: flags field + * Rm: source register + */ +#define ARMV4_5_MSR_GP(Rm, Field, R) (0xe120f000 | Rm | (Field << 16) | (R << 22)) +#define ARMV4_5_MSR_IM(Im, Rotate, Field, R) (0xe320f000 | (Im) | (Rotate << 8) | (Field << 16) | (R << 22)) + +/* Load Register Halfword Immediate Post-Index + * Rd: register to load + * Rn: base register + */ +#define ARMV4_5_LDRH_IP(Rd, Rn) (0xe0d000b2 | (Rd << 12) | (Rn << 16)) + +/* Load Register Byte Immediate Post-Index + * Rd: register to load + * Rn: base register + */ +#define ARMV4_5_LDRB_IP(Rd, Rn) (0xe4d00001 | (Rd << 12) | (Rn << 16)) + +/* Store register Halfword Immediate Post-Index + * Rd: register to store + * Rn: base register + */ +#define ARMV4_5_STRH_IP(Rd, Rn) (0xe0c000b2 | (Rd << 12) | (Rn << 16)) + +/* Store register Byte Immediate Post-Index + * Rd: register to store + * Rn: base register + */ +#define ARMV4_5_STRB_IP(Rd, Rn) (0xe4c00001 | (Rd << 12) | (Rn << 16)) + +/* Branch (and Link) + * Im: Branch target (left-shifted by 2 bits, added to PC) + * L: 1: branch and link 0: branch only + */ +#define ARMV4_5_B(Im, L) (0xea000000 | Im | (L << 24)) + +/* Branch and exchange (ARM state) + * Rm: register holding branch target address + */ +#define ARMV4_5_BX(Rm) (0xe12fff10 | Rm) + +/* Thumb mode instructions + */ + +/* Store register (Thumb mode) + * Rd: source register + * Rn: base register + */ +#define ARMV4_5_T_STR(Rd, Rn) ((0x6000 | Rd | (Rn << 3)) | ((0x6000 | Rd | (Rn << 3)) << 16)) + +/* Load register (Thumb state) + * Rd: destination register + * Rn: base register + */ +#define ARMV4_5_T_LDR(Rd, Rn) ((0x6800 | (Rn << 3) | Rd) | ((0x6800 | (Rn << 3) | Rd) << 16)) + +/* Move hi register (Thumb mode) + * Rd: destination register + * Rm: source register + */ +#define ARMV4_5_T_MOV(Rd, Rm) ((0x4600 | (Rd & 0x7) | ((Rd & 0x8) << 4) | ((Rm & 0x7) << 3) | ((Rm & 0x8) << 3)) | ((0x4600 | (Rd & 0x7) | ((Rd & 0x8) << 4) | ((Rm & 0x7) << 3) | ((Rm & 0x8) << 3)) << 16)) + +/* No operation (Thumb mode) + */ +#define ARMV4_5_T_NOP (0x1c3f | (0x1c3f << 16)) + +/* Move immediate to register (Thumb state) + * Rd: destination register + * Im: 8-bit immediate value + */ +#define ARMV4_5_T_MOV_IM(Rd, Im) ((0x2000 | (Rd << 8) | Im) | ((0x2000 | (Rd << 8) | Im) << 16)) + +/* Branch and Exchange + * Rm: register containing branch target + */ +#define ARMV4_5_T_BX(Rm) ((0x4700 | (Rm << 3)) | ((0x4700 | (Rm << 3)) << 16)) + +/* Branch (Thumb state) + * Imm: Branch target + */ +#define ARMV4_5_T_B(Imm) ((0xe000 | Imm) | ((0xe000 | Imm) << 16)) + +#endif /* ARMV4_5_H */ diff --git a/src/target/armv4_5_cache.c b/src/target/armv4_5_cache.c new file mode 100644 index 00000000..326f10ee --- /dev/null +++ b/src/target/armv4_5_cache.c @@ -0,0 +1,112 @@ +/*************************************************************************** + * 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 "armv4_5_cache.h" + +#include "log.h" +#include "command.h" + +int armv4_5_identify_cache(u32 cache_type_reg, armv4_5_cache_common_t *cache) +{ + int size, assoc, M, len, multiplier; + + cache->ctype = (cache_type_reg & 0x1e000000U) >> 25; + cache->separate = (cache_type_reg & 0x01000000U) >> 24; + + size = (cache_type_reg & 0x1c0000) >> 18; + assoc = (cache_type_reg & 0x38000) >> 15; + M = (cache_type_reg & 0x4000) >> 14; + len = (cache_type_reg & 0x3000) >> 12; + multiplier = 2 + M; + + if ((assoc != 0) || (M != 1)) /* assoc 0 and M 1 means cache absent */ + { + /* cache is present */ + cache->d_u_size.linelen = 1 << (len + 3); + cache->d_u_size.associativity = multiplier << (assoc - 1); + cache->d_u_size.nsets = 1 << (size + 6 - assoc - len); + cache->d_u_size.cachesize = multiplier << (size + 8); + } + else + { + /* cache is absent */ + cache->d_u_size.linelen = -1; + cache->d_u_size.associativity = -1; + cache->d_u_size.nsets = -1; + cache->d_u_size.cachesize = -1; + } + + if (cache->separate) + { + size = (cache_type_reg & 0x1c0) >> 6; + assoc = (cache_type_reg & 0x38) >> 3; + M = (cache_type_reg & 0x4) >> 2; + len = (cache_type_reg & 0x3); + multiplier = 2 + M; + + if ((assoc != 0) || (M != 1)) /* assoc 0 and M 1 means cache absent */ + { + /* cache is present */ + cache->i_size.linelen = 1 << (len + 3); + cache->i_size.associativity = multiplier << (assoc - 1); + cache->i_size.nsets = 1 << (size + 6 - assoc - len); + cache->i_size.cachesize = multiplier << (size + 8); + } + else + { + /* cache is absent */ + cache->i_size.linelen = -1; + cache->i_size.associativity = -1; + cache->i_size.nsets = -1; + cache->i_size.cachesize = -1; + } + } + else + { + cache->i_size = cache->d_u_size; + } + + return ERROR_OK; +} + +int armv4_5_handle_cache_info_command(struct command_context_s *cmd_ctx, armv4_5_cache_common_t *armv4_5_cache) +{ + if (armv4_5_cache->ctype == -1) + { + command_print(cmd_ctx, "cache not yet identified"); + return ERROR_OK; + } + + command_print(cmd_ctx, "cache type: 0x%1.1x, %s", armv4_5_cache->ctype, + (armv4_5_cache->separate) ? "separate caches" : "unified cache"); + + command_print(cmd_ctx, "D-Cache: linelen %i, associativity %i, nsets %i, cachesize 0x%x", + armv4_5_cache->d_u_size.linelen, + armv4_5_cache->d_u_size.associativity, + armv4_5_cache->d_u_size.nsets, + armv4_5_cache->d_u_size.cachesize); + + command_print(cmd_ctx, "I-Cache: linelen %i, associativity %i, nsets %i, cachesize 0x%x", + armv4_5_cache->i_size.linelen, + armv4_5_cache->i_size.associativity, + armv4_5_cache->i_size.nsets, + armv4_5_cache->i_size.cachesize); + + return ERROR_OK; +} diff --git a/src/target/armv4_5_cache.h b/src/target/armv4_5_cache.h new file mode 100644 index 00000000..766718b6 --- /dev/null +++ b/src/target/armv4_5_cache.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARMV4_5_CACHE_H +#define ARMV4_5_CACHE_H + +#include "types.h" +#include "command.h" + +typedef struct armv4_5_cachesize_s +{ + int linelen; + int associativity; + int nsets; + int cachesize; +} armv4_5_cachesize_t; + +typedef struct armv4_5_cache_common_s +{ + int ctype; /* specify supported cache operations */ + int separate; /* separate caches or unified cache */ + armv4_5_cachesize_t d_u_size; /* data cache */ + armv4_5_cachesize_t i_size; /* instruction cache */ + int i_cache_enabled; + int d_u_cache_enabled; +} armv4_5_cache_common_t; + +extern int armv4_5_identify_cache(u32 cache_type_reg, armv4_5_cache_common_t *cache); +extern int armv4_5_cache_state(u32 cp15_control_reg, armv4_5_cache_common_t *cache); + +extern int armv4_5_handle_cache_info_command(struct command_context_s *cmd_ctx, armv4_5_cache_common_t *armv4_5_cache); + +#endif /* ARMV4_5_CACHE_H */ diff --git a/src/target/armv4_5_mmu.c b/src/target/armv4_5_mmu.c new file mode 100644 index 00000000..c7dad3e2 --- /dev/null +++ b/src/target/armv4_5_mmu.c @@ -0,0 +1,358 @@ +/*************************************************************************** + * 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 "arm7_9_common.h" +#include "log.h" +#include "command.h" +#include "armv4_5_mmu.h" + +#include + +u32 armv4mmu_translate_va(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 va, int *type, u32 *cb, int *domain, u32 *ap); +int armv4_5_mmu_read_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); +int armv4_5_mmu_write_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); + +char* armv4_5_mmu_page_type_names[] = +{ + "section", "large page", "small page", "tiny page" +}; + +u32 armv4_5_mmu_translate_va(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 va, int *type, u32 *cb, int *domain, u32 *ap) +{ + u32 first_lvl_descriptor = 0x0; + u32 second_lvl_descriptor = 0x0; + u32 ttb = armv4_5_mmu->get_ttb(target); + + armv4_5_mmu_read_physical(target, armv4_5_mmu, + (ttb & 0xffffc000) | ((va & 0xfff00000) >> 18), + 4, 1, (u8*)&first_lvl_descriptor); + + DEBUG("1st lvl desc: %8.8x", first_lvl_descriptor); + + if ((first_lvl_descriptor & 0x3) == 0) + { + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; + } + + if (!armv4_5_mmu->has_tiny_pages && ((first_lvl_descriptor & 0x3) == 3)) + { + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; + } + + /* domain is always specified in bits 8-5 */ + *domain = (first_lvl_descriptor & 0x1e0) >> 5; + + if ((first_lvl_descriptor & 0x3) == 2) + { + /* section descriptor */ + *type = ARMV4_5_SECTION; + *cb = (first_lvl_descriptor & 0xc) >> 2; + *ap = (first_lvl_descriptor & 0xc00) >> 10; + return (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff); + } + + if ((first_lvl_descriptor & 0x3) == 1) + { + /* coarse page table */ + armv4_5_mmu_read_physical(target, armv4_5_mmu, + (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10), + 4, 1, (u8*)&second_lvl_descriptor); + } + + if ((first_lvl_descriptor & 0x3) == 3) + { + /* fine page table */ + armv4_5_mmu_read_physical(target, armv4_5_mmu, + (first_lvl_descriptor & 0xfffff000) | ((va & 0x000ffc00) >> 8), + 4, 1, (u8*)&second_lvl_descriptor); + } + + DEBUG("2nd lvl desc: %8.8x", first_lvl_descriptor); + + if ((second_lvl_descriptor & 0x3) == 0) + { + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; + } + + /* cacheable/bufferable is always specified in bits 3-2 */ + *cb = (second_lvl_descriptor & 0xc) >> 2; + + if ((second_lvl_descriptor & 0x3) == 1) + { + /* large page descriptor */ + *type = ARMV4_5_LARGE_PAGE; + *ap = (second_lvl_descriptor & 0xff0) >> 4; + return (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff); + } + + if ((second_lvl_descriptor & 0x3) == 2) + { + /* small page descriptor */ + *type = ARMV4_5_SMALL_PAGE; + *ap = (second_lvl_descriptor & 0xff0) >> 4; + return (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff); + } + + if ((second_lvl_descriptor & 0x3) == 3) + { + /* tiny page descriptor */ + *type = ARMV4_5_TINY_PAGE; + *ap = (second_lvl_descriptor & 0x30) >> 4; + return (second_lvl_descriptor & 0xfffffc00) | (va & 0x000003ff); + } + + /* should not happen */ + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; +} + +int armv4_5_mmu_read_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + /* disable MMU and data (or unified) cache */ + armv4_5_mmu->disable_mmu_caches(target, 1, 1, 0); + + retval = armv4_5_mmu->read_memory(target, address, size, count, buffer); + + /* reenable MMU / cache */ + armv4_5_mmu->enable_mmu_caches(target, armv4_5_mmu->mmu_enabled, + armv4_5_mmu->armv4_5_cache.d_u_cache_enabled, + armv4_5_mmu->armv4_5_cache.i_cache_enabled); + + return retval; +} + +int armv4_5_mmu_write_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + /* disable MMU and data (or unified) cache */ + armv4_5_mmu->disable_mmu_caches(target, 1, 1, 0); + + retval = armv4_5_mmu->write_memory(target, address, size, count, buffer); + + /* reenable MMU / cache */ + armv4_5_mmu->enable_mmu_caches(target, armv4_5_mmu->mmu_enabled, + armv4_5_mmu->armv4_5_cache.d_u_cache_enabled, + armv4_5_mmu->armv4_5_cache.i_cache_enabled); + + return retval; +} + +int armv4_5_mmu_handle_virt2phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu) +{ + u32 va; + u32 pa; + int type; + u32 cb; + int domain; + u32 ap; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"virt2phys\" command"); + return ERROR_OK; + } + + if (argc == 0) + { + command_print(cmd_ctx, "usage: virt2phys "); + return ERROR_OK; + } + + if (argc == 1) + { + va = strtoul(args[0], NULL, 0); + pa = armv4_5_mmu_translate_va(target, armv4_5_mmu, va, &type, &cb, &domain, &ap); + if (type == -1) + { + switch (pa) + { + case ERROR_TARGET_TRANSLATION_FAULT: + command_print(cmd_ctx, "no valid translation for 0x%8.8x", va); + break; + default: + command_print(cmd_ctx, "unknown translation error"); + } + return ERROR_OK; + } + + command_print(cmd_ctx, "0x%8.8x -> 0x%8.8x, type: %s, cb: %i, domain: %i, ap: %2.2x", + va, pa, armv4_5_mmu_page_type_names[type], cb, domain, ap); + } + + return ERROR_OK; +} + +int armv4_5_mmu_handle_md_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu) +{ + int count = 1; + int size = 4; + u32 address = 0; + int i; + + char output[128]; + int output_len; + + int retval; + + u8 *buffer; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + 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 = armv4_5_mmu_read_physical(target, armv4_5_mmu, 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"); + } + } + + 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 armv4_5_mmu_handle_mw_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu) +{ + u32 address = 0; + u32 value = 0; + int retval; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + if (argc < 2) + return ERROR_OK; + + address = strtoul(args[0], NULL, 0); + value = strtoul(args[1], NULL, 0); + + switch (cmd[2]) + { + case 'w': + retval = armv4_5_mmu_write_physical(target, armv4_5_mmu, address, 4, 1, (u8*)&value); + break; + case 'h': + retval = armv4_5_mmu_write_physical(target, armv4_5_mmu, address, 2, 1, (u8*)&value); + break; + case 'b': + retval = armv4_5_mmu_write_physical(target, armv4_5_mmu, 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"); + } + + return ERROR_OK; +} diff --git a/src/target/armv4_5_mmu.h b/src/target/armv4_5_mmu.h new file mode 100644 index 00000000..3adb30e2 --- /dev/null +++ b/src/target/armv4_5_mmu.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ARMV4_5_MMU_H +#define ARMV4_5_MMU_H + +#include "armv4_5_cache.h" + +typedef struct armv4_5_mmu_common_s +{ + u32 (*get_ttb)(target_t *target); + int (*read_memory)(target_t *target, u32 address, u32 size, u32 count, u8 *buffer); + int (*write_memory)(target_t *target, u32 address, u32 size, u32 count, u8 *buffer); + void (*disable_mmu_caches)(target_t *target, int mmu, int d_u_cache, int i_cache); + void (*enable_mmu_caches)(target_t *target, int mmu, int d_u_cache, int i_cache); + armv4_5_cache_common_t armv4_5_cache; + int has_tiny_pages; + int mmu_enabled; +} armv4_5_mmu_common_t; + +enum +{ + ARMV4_5_SECTION, ARMV4_5_LARGE_PAGE, ARMV4_5_SMALL_PAGE, ARMV4_5_TINY_PAGE +}; + +extern char* armv4_5_page_type_names[]; + +extern u32 armv4_5_mmu_translate_va(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 va, int *type, u32 *cb, int *domain, u32 *ap); +extern int armv4_5_mmu_read_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); +extern int armv4_5_mmu_write_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); + +extern int armv4_5_mmu_handle_virt2phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu); +extern int armv4_5_mmu_handle_md_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu); +extern int armv4_5_mmu_handle_mw_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu); + +#endif /* ARMV4_5_MMU_H */ diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c new file mode 100644 index 00000000..efba25c9 --- /dev/null +++ b/src/target/breakpoints.c @@ -0,0 +1,219 @@ +/*************************************************************************** + * 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 + +#include "binarybuffer.h" +#include "target.h" +#include "log.h" +#include "types.h" + +#include "breakpoints.h" + +char *breakpoint_type_strings[] = +{ + "hardware", + "software" +}; + +char *watchpoint_rw_strings[] = +{ + "read", + "write", + "access" +}; + +int breakpoint_add(target_t *target, u32 address, u32 length, enum breakpoint_type type) +{ + breakpoint_t *breakpoint = target->breakpoints; + breakpoint_t **breakpoint_p = &target->breakpoints; + int retval; + + while (breakpoint) + { + if (breakpoint->address == address) + return ERROR_OK; + breakpoint_p = &breakpoint->next; + breakpoint = breakpoint->next; + } + + if ((retval = target->type->add_breakpoint(target, address, length, type)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: + INFO("can't add %s breakpoint, resource not available", breakpoint_type_strings[type]); + return retval; + break; + case ERROR_TARGET_NOT_HALTED: + INFO("can't add breakpoint while target is running"); + return retval; + break; + default: + ERROR("unknown error"); + exit(-1); + break; + } + } + + (*breakpoint_p) = malloc(sizeof(breakpoint_t)); + (*breakpoint_p)->address = address; + (*breakpoint_p)->length = length; + (*breakpoint_p)->type = type; + (*breakpoint_p)->set = 0; + (*breakpoint_p)->orig_instr = malloc(CEIL(length, 8)); + (*breakpoint_p)->next = NULL; + + DEBUG("added %s breakpoint at 0x%8.8x of length 0x%8.8x", breakpoint_type_strings[type], address, length); + + return ERROR_OK; +} + +int breakpoint_remove(target_t *target, u32 address) +{ + breakpoint_t *breakpoint = target->breakpoints; + breakpoint_t **breakpoint_p = &target->breakpoints; + int retval; + + while (breakpoint) + { + if (breakpoint->address == address) + break; + breakpoint_p = &breakpoint->next; + breakpoint = breakpoint->next; + } + + if (breakpoint) + { + if ((retval = target->type->remove_breakpoint(target, breakpoint)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + INFO("can't remove breakpoint while target is running"); + return retval; + break; + default: + ERROR("unknown error"); + exit(-1); + break; + } + } + (*breakpoint_p) = breakpoint->next; + free(breakpoint->orig_instr); + free(breakpoint); + } + else + { + ERROR("no breakpoint at address 0x%8.8x found", address); + } + + return ERROR_OK; +} + +breakpoint_t* breakpoint_find(target_t *target, u32 address) +{ + breakpoint_t *breakpoint = target->breakpoints; + + while (breakpoint) + { + if (breakpoint->address == address) + return breakpoint; + breakpoint = breakpoint->next; + } + + return NULL; +} + +int watchpoint_add(target_t *target, u32 address, u32 length, enum watchpoint_rw rw, u32 value, u32 mask) +{ + watchpoint_t *watchpoint = target->watchpoints; + watchpoint_t **watchpoint_p = &target->watchpoints; + int retval; + + while (watchpoint) + { + if (watchpoint->address == address) + return ERROR_OK; + watchpoint_p = &watchpoint->next; + watchpoint = watchpoint->next; + } + + if ((retval = target->type->add_watchpoint(target, address, length, rw)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: + INFO("can't add %s watchpoint, resource not available", watchpoint_rw_strings[rw]); + return retval; + break; + default: + ERROR("unknown error"); + exit(-1); + break; + } + } + + (*watchpoint_p) = malloc(sizeof(watchpoint_t)); + (*watchpoint_p)->address = address; + (*watchpoint_p)->length = length; + (*watchpoint_p)->value = value; + (*watchpoint_p)->mask = mask; + (*watchpoint_p)->rw = rw; + (*watchpoint_p)->set = 0; + (*watchpoint_p)->next = NULL; + + DEBUG("added %s watchpoint at 0x%8.8x of length 0x%8.8x", watchpoint_rw_strings[rw], address, length); + + return ERROR_OK; +} + +int watchpoint_remove(target_t *target, u32 address) +{ + watchpoint_t *watchpoint = target->watchpoints; + watchpoint_t **watchpoint_p = &target->watchpoints; + int retval; + + while (watchpoint) + { + if (watchpoint->address == address) + break; + watchpoint_p = &watchpoint->next; + watchpoint = watchpoint->next; + } + + if (watchpoint) + { + if ((retval = target->type->remove_watchpoint(target, watchpoint)) != ERROR_OK) + { + ERROR("BUG: can't remove watchpoint"); + exit(-1); + } + (*watchpoint_p) = watchpoint->next; + free(watchpoint); + } + else + { + ERROR("no watchpoint at address 0x%8.8x found", address); + } + + return ERROR_OK; +} diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h new file mode 100644 index 00000000..7eba39aa --- /dev/null +++ b/src/target/breakpoints.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef BREAKPOINTS_H +#define BREAKPOINTS_H + +#include "target.h" + +struct target_s; + +enum breakpoint_type +{ + BKPT_HARD, + BKPT_SOFT, +}; + +extern char *breakpoint_type_strings[]; + +enum watchpoint_rw +{ + WPT_READ = 0, WPT_WRITE = 1, WPT_ACCESS = 2 +}; + +extern char *watchpoint_rw_strings[]; + +typedef struct breakpoint_s +{ + u32 address; + int length; + enum breakpoint_type type; + int set; + u8 *orig_instr; + struct breakpoint_s *next; +} breakpoint_t; + +typedef struct watchpoint_s +{ + u32 address; + int length; + u32 mask; + u32 value; + enum watchpoint_rw rw; + int set; + struct watchpoint_s *next; +} watchpoint_t; + +extern int breakpoint_add(struct target_s *target, u32 address, u32 length, enum breakpoint_type type); +extern int breakpoint_remove(struct target_s *target, u32 address); +extern breakpoint_t* breakpoint_find(struct target_s *target, u32 address); +extern int watchpoint_add(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw, u32 value, u32 mask); +extern int watchpoint_remove(struct target_s *target, u32 address); + +#endif /* BREAKPOINTS_H */ + diff --git a/src/target/embeddedice.c b/src/target/embeddedice.c new file mode 100644 index 00000000..e148b888 --- /dev/null +++ b/src/target/embeddedice.c @@ -0,0 +1,301 @@ +/*************************************************************************** + * 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 "embeddedice.h" + +#include "armv4_5.h" +#include "arm7_9_common.h" + +#include "log.h" +#include "arm_jtag.h" +#include "types.h" +#include "binarybuffer.h" +#include "target.h" +#include "register.h" +#include "jtag.h" + +#include + +bitfield_desc_t embeddedice_comms_ctrl_bitfield_desc[] = +{ + {"R", 1}, + {"W", 1}, + {"reserved", 26}, + {"version", 4} +}; + +int embeddedice_reg_arch_info[] = +{ + 0x0, 0x1, 0x4, 0x5, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 +}; + +char* embeddedice_reg_list[] = +{ + "debug_ctrl", + "debug_status", + + "comms_ctrl", + "comms_data", + + "watch 0 addr value", + "watch 0 addr mask", + "watch 0 data value", + "watch 0 data mask", + "watch 0 control value", + "watch 0 control mask", + + "watch 1 addr value", + "watch 1 addr mask", + "watch 1 data value", + "watch 1 data mask", + "watch 1 control value", + "watch 1 control mask" +}; + +int embeddedice_reg_arch_type = -1; + +int embeddedice_get_reg(reg_t *reg); +int embeddedice_set_reg(reg_t *reg, u32 value); + +int embeddedice_write_reg(reg_t *reg, u32 value); +int embeddedice_read_reg(reg_t *reg); + +reg_cache_t* embeddedice_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg) +{ + reg_cache_t *reg_cache = malloc(sizeof(reg_cache_t)); + reg_t *reg_list = NULL; + embeddedice_reg_t *arch_info = NULL; + int num_regs = 16 + extra_reg; + int i; + + /* register a register arch-type for EmbeddedICE registers only once */ + if (embeddedice_reg_arch_type == -1) + embeddedice_reg_arch_type = register_reg_arch_type(embeddedice_get_reg, embeddedice_set_reg_w_exec); + + /* the actual registers are kept in two arrays */ + reg_list = calloc(num_regs, sizeof(reg_t)); + arch_info = calloc(num_regs, sizeof(embeddedice_reg_t)); + + /* fill in values for the reg cache */ + reg_cache->name = "EmbeddedICE registers"; + reg_cache->next = NULL; + reg_cache->reg_list = reg_list; + reg_cache->num_regs = num_regs; + + /* set up registers */ + for (i = 0; i < num_regs - extra_reg; i++) + { + reg_list[i].name = embeddedice_reg_list[i]; + reg_list[i].size = 32; + reg_list[i].dirty = 0; + reg_list[i].valid = 0; + reg_list[i].bitfield_desc = NULL; + reg_list[i].num_bitfields = 0; + reg_list[i].value = calloc(1, 4); + reg_list[i].arch_info = &arch_info[i]; + reg_list[i].arch_type = embeddedice_reg_arch_type; + arch_info[i].addr = embeddedice_reg_arch_info[i]; + arch_info[i].jtag_info = jtag_info; + } + + /* there may be one extra reg (Abort status (ARM7 rev4) or Vector catch (ARM9)) */ + if (extra_reg) + { + reg_list[num_regs - 1].arch_info = &arch_info[num_regs - 1]; + arch_info[num_regs - 1].jtag_info = jtag_info; + } + + return reg_cache; +} + +int embeddedice_get_reg(reg_t *reg) +{ + if (embeddedice_read_reg(reg) != ERROR_OK) + { + ERROR("BUG: error scheduling EmbeddedICE register read"); + exit(-1); + } + + if (jtag_execute_queue() != ERROR_OK) + { + ERROR("register read failed"); + } + + return ERROR_OK; +} + +int embeddedice_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask) +{ + embeddedice_reg_t *ice_reg = reg->arch_info; + u8 reg_addr = ice_reg->addr & 0x1f; + scan_field_t fields[3]; + + DEBUG("%i", ice_reg->addr); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(ice_reg->jtag_info, 0x2); + arm_jtag_set_instr(ice_reg->jtag_info, ice_reg->jtag_info->intest_instr); + + fields[0].device = ice_reg->jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = reg->value; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = ice_reg->jtag_info->chain_pos; + fields[1].num_bits = 5; + fields[1].out_value = malloc(1); + buf_set_u32(fields[1].out_value, 0, 5, reg_addr); + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = ice_reg->jtag_info->chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = malloc(1); + buf_set_u32(fields[2].out_value, 0, 1, 0); + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + fields[0].in_value = reg->value; + fields[0].in_check_value = check_value; + fields[0].in_check_mask = check_mask; + + /* when reading the DCC data register, leaving the address field set to + * EICE_COMMS_DATA would read the register twice + * reading the control register is safe + */ + buf_set_u32(fields[1].out_value, 0, 5, embeddedice_reg_arch_info[EICE_COMMS_CTRL]); + + jtag_add_dr_scan(3, fields, -1); + + free(fields[1].out_value); + free(fields[2].out_value); + + return ERROR_OK; +} + +int embeddedice_read_reg(reg_t *reg) +{ + return embeddedice_read_reg_w_check(reg, NULL, NULL); +} + +int embeddedice_set_reg(reg_t *reg, u32 value) +{ + if (embeddedice_write_reg(reg, value) != ERROR_OK) + { + ERROR("BUG: error scheduling EmbeddedICE register write"); + exit(-1); + } + + buf_set_u32(reg->value, 0, reg->size, value); + reg->valid = 1; + reg->dirty = 0; + + return ERROR_OK; +} + +int embeddedice_set_reg_w_exec(reg_t *reg, u32 value) +{ + embeddedice_set_reg(reg, value); + + if (jtag_execute_queue() != ERROR_OK) + { + ERROR("register write failed"); + exit(-1); + } + return ERROR_OK; +} + +int embeddedice_write_reg(reg_t *reg, u32 value) +{ + embeddedice_reg_t *ice_reg = reg->arch_info; + u8 reg_addr = ice_reg->addr & 0x1f; + scan_field_t fields[3]; + + DEBUG("%i: 0x%8.8x", ice_reg->addr, value); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(ice_reg->jtag_info, 0x2); + arm_jtag_set_instr(ice_reg->jtag_info, ice_reg->jtag_info->intest_instr); + + fields[0].device = ice_reg->jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = malloc(4); + buf_set_u32(fields[0].out_value, 0, 32, value); + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = ice_reg->jtag_info->chain_pos; + fields[1].num_bits = 5; + fields[1].out_value = malloc(1); + buf_set_u32(fields[1].out_value, 0, 5, reg_addr); + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = ice_reg->jtag_info->chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = malloc(1); + buf_set_u32(fields[2].out_value, 0, 1, 1); + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + free(fields[0].out_value); + free(fields[1].out_value); + free(fields[2].out_value); + + return ERROR_OK; +} + +int embeddedice_store_reg(reg_t *reg) +{ + return embeddedice_write_reg(reg, buf_get_u32(reg->value, 0, reg->size)); +} + diff --git a/src/target/embeddedice.h b/src/target/embeddedice.h new file mode 100644 index 00000000..0062153f --- /dev/null +++ b/src/target/embeddedice.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef EMBEDDED_ICE_H +#define EMBEDDED_ICE_H + +#include "target.h" +#include "register.h" +#include "arm_jtag.h" + +enum +{ + EICE_DBG_CTRL = 0, + EICE_DBG_STAT = 1, + EICE_COMMS_CTRL = 2, + EICE_COMMS_DATA = 3, + EICE_W0_ADDR_VALUE = 4, + EICE_W0_ADDR_MASK = 5, + EICE_W0_DATA_VALUE = 6, + EICE_W0_DATA_MASK = 7, + EICE_W0_CONTROL_VALUE = 8, + EICE_W0_CONTROL_MASK = 9, + EICE_W1_ADDR_VALUE = 10, + EICE_W1_ADDR_MASK = 11, + EICE_W1_DATA_VALUE = 12, + EICE_W1_DATA_MASK = 13, + EICE_W1_CONTROL_VALUE = 14, + EICE_W1_CONTROL_MASK = 15 +}; + +enum +{ + EICE_DBG_CONTROL_INTDIS = 2, + EICE_DBG_CONTROL_DBGRQ = 1, + EICE_DBG_CONTROL_DBGACK = 0, +}; + +enum +{ + EICE_DBG_STATUS_ITBIT = 4, + EICE_DBG_STATUS_SYSCOMP = 3, + EICE_DBG_STATUS_IFEN = 2, + EICE_DBG_STATUS_DBGRQ = 1, + EICE_DBG_STATUS_DBGACK = 0 +}; + +enum +{ + EICE_W_CTRL_ENABLE = 0x100, + EICE_W_CTRL_RANGE = 0x80, + EICE_W_CTRL_CHAIN = 0x40, + EICE_W_CTRL_EXTERN = 0x20, + EICE_W_CTRL_nTRANS = 0x10, + EICE_W_CTRL_nOPC = 0x8, + EICE_W_CTRL_MAS = 0x6, + EICE_W_CTRL_ITBIT = 0x2, + EICE_W_CTRL_nRW = 0x1 +}; + +typedef struct embeddedice_reg_s +{ + int addr; + arm_jtag_t *jtag_info; +} embeddedice_reg_t; + +extern reg_cache_t* embeddedice_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg); +extern int embeddedice_read_reg(reg_t *reg); +extern int embeddedice_write_reg(reg_t *reg, u32 value); +extern int embeddedice_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask); +extern int embeddedice_store_reg(reg_t *reg); +extern int embeddedice_set_reg(reg_t *reg, u32 value); +extern int embeddedice_set_reg_w_exec(reg_t *reg, u32 value); + +#endif /* EMBEDDED_ICE_H */ diff --git a/src/target/etm.c b/src/target/etm.c new file mode 100644 index 00000000..a84e2d67 --- /dev/null +++ b/src/target/etm.c @@ -0,0 +1,409 @@ +/*************************************************************************** + * 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 "etm.h" + +#include "armv4_5.h" +#include "arm7_9_common.h" + +#include "log.h" +#include "arm_jtag.h" +#include "types.h" +#include "binarybuffer.h" +#include "target.h" +#include "register.h" +#include "jtag.h" + +#include + +bitfield_desc_t etm_comms_ctrl_bitfield_desc[] = +{ + {"R", 1}, + {"W", 1}, + {"reserved", 26}, + {"version", 4} +}; + +int etm_reg_arch_info[] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, +}; + +int etm_reg_arch_size_info[] = +{ + 32, 32, 17, 8, 3, 9, 32, 17, + 26, 16, 25, 8, 17, 32, 32, 17, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 16, 16, 16, 16, 18, 18, 18, 18, + 17, 17, 17, 17, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 2, + 17, 17, 17, 17, 32, 32, 32, 32 +}; + +char* etm_reg_list[] = +{ + "ETM_CTRL", + "ETM_CONFIG", + "ETM_TRIG_EVENT", + "ETM_MMD_CTRL", + "ETM_STATUS", + "ETM_SYS_CONFIG", + "ETM_TRACE_RESOURCE_CTRL", + "ETM_TRACE_EN_CTRL2", + "ETM_TRACE_EN_EVENT", + "ETM_TRACE_EN_CTRL1", + "ETM_FIFOFULL_REGION", + "ETM_FIFOFULL_LEVEL", + "ETM_VIEWDATA_EVENT", + "ETM_VIEWDATA_CTRL1", + "ETM_VIEWDATA_CTRL2", + "ETM_VIEWDATA_CTRL3", + "ETM_ADDR_COMPARATOR_VALUE1", + "ETM_ADDR_COMPARATOR_VALUE2", + "ETM_ADDR_COMPARATOR_VALUE3", + "ETM_ADDR_COMPARATOR_VALUE4", + "ETM_ADDR_COMPARATOR_VALUE5", + "ETM_ADDR_COMPARATOR_VALUE6", + "ETM_ADDR_COMPARATOR_VALUE7", + "ETM_ADDR_COMPARATOR_VALUE8", + "ETM_ADDR_COMPARATOR_VALUE9", + "ETM_ADDR_COMPARATOR_VALUE10", + "ETM_ADDR_COMPARATOR_VALUE11", + "ETM_ADDR_COMPARATOR_VALUE12", + "ETM_ADDR_COMPARATOR_VALUE13", + "ETM_ADDR_COMPARATOR_VALUE14", + "ETM_ADDR_COMPARATOR_VALUE15", + "ETM_ADDR_COMPARATOR_VALUE16", + "ETM_ADDR_ACCESS_TYPE1", + "ETM_ADDR_ACCESS_TYPE2", + "ETM_ADDR_ACCESS_TYPE3", + "ETM_ADDR_ACCESS_TYPE4", + "ETM_ADDR_ACCESS_TYPE5", + "ETM_ADDR_ACCESS_TYPE6", + "ETM_ADDR_ACCESS_TYPE7", + "ETM_ADDR_ACCESS_TYPE8", + "ETM_ADDR_ACCESS_TYPE9", + "ETM_ADDR_ACCESS_TYPE10", + "ETM_ADDR_ACCESS_TYPE11", + "ETM_ADDR_ACCESS_TYPE12", + "ETM_ADDR_ACCESS_TYPE13", + "ETM_ADDR_ACCESS_TYPE14", + "ETM_ADDR_ACCESS_TYPE15", + "ETM_ADDR_ACCESS_TYPE16", + "ETM_DATA_COMPARATOR_VALUE1", + "ETM_DATA_COMPARATOR_VALUE2", + "ETM_DATA_COMPARATOR_VALUE3", + "ETM_DATA_COMPARATOR_VALUE4", + "ETM_DATA_COMPARATOR_VALUE5", + "ETM_DATA_COMPARATOR_VALUE6", + "ETM_DATA_COMPARATOR_VALUE7", + "ETM_DATA_COMPARATOR_VALUE8", + "ETM_DATA_COMPARATOR_VALUE9", + "ETM_DATA_COMPARATOR_VALUE10", + "ETM_DATA_COMPARATOR_VALUE11", + "ETM_DATA_COMPARATOR_VALUE12", + "ETM_DATA_COMPARATOR_VALUE13", + "ETM_DATA_COMPARATOR_VALUE14", + "ETM_DATA_COMPARATOR_VALUE15", + "ETM_DATA_COMPARATOR_VALUE16", + "ETM_DATA_COMPARATOR_MASK1", + "ETM_DATA_COMPARATOR_MASK2", + "ETM_DATA_COMPARATOR_MASK3", + "ETM_DATA_COMPARATOR_MASK4", + "ETM_DATA_COMPARATOR_MASK5", + "ETM_DATA_COMPARATOR_MASK6", + "ETM_DATA_COMPARATOR_MASK7", + "ETM_DATA_COMPARATOR_MASK8", + "ETM_DATA_COMPARATOR_MASK9", + "ETM_DATA_COMPARATOR_MASK10", + "ETM_DATA_COMPARATOR_MASK11", + "ETM_DATA_COMPARATOR_MASK12", + "ETM_DATA_COMPARATOR_MASK13", + "ETM_DATA_COMPARATOR_MASK14", + "ETM_DATA_COMPARATOR_MASK15", + "ETM_DATA_COMPARATOR_MASK16", + "ETM_COUNTER_INITAL_VALUE1", + "ETM_COUNTER_INITAL_VALUE2", + "ETM_COUNTER_INITAL_VALUE3", + "ETM_COUNTER_INITAL_VALUE4", + "ETM_COUNTER_ENABLE1", + "ETM_COUNTER_ENABLE2", + "ETM_COUNTER_ENABLE3", + "ETM_COUNTER_ENABLE4", + "ETM_COUNTER_RELOAD_VALUE1", + "ETM_COUNTER_RELOAD_VALUE2", + "ETM_COUNTER_RELOAD_VALUE3", + "ETM_COUNTER_RELOAD_VALUE4", + "ETM_COUNTER_VALUE1", + "ETM_COUNTER_VALUE2", + "ETM_COUNTER_VALUE3", + "ETM_COUNTER_VALUE4", + "ETM_SEQUENCER_CTRL1", + "ETM_SEQUENCER_CTRL2", + "ETM_SEQUENCER_CTRL3", + "ETM_SEQUENCER_CTRL4", + "ETM_SEQUENCER_CTRL5", + "ETM_SEQUENCER_CTRL6", + "ETM_SEQUENCER_STATE", + "ETM_EXTERNAL_OUTPUT1", + "ETM_EXTERNAL_OUTPUT2", + "ETM_EXTERNAL_OUTPUT3", + "ETM_EXTERNAL_OUTPUT4", + "ETM_CONTEXTID_COMPARATOR_VALUE1", + "ETM_CONTEXTID_COMPARATOR_VALUE2", + "ETM_CONTEXTID_COMPARATOR_VALUE3", + "ETM_CONTEXTID_COMPARATOR_MASK" +}; + +int etm_reg_arch_type = -1; + +int etm_get_reg(reg_t *reg); +int etm_set_reg(reg_t *reg, u32 value); + +int etm_write_reg(reg_t *reg, u32 value); +int etm_read_reg(reg_t *reg); + +reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg) +{ + reg_cache_t *reg_cache = malloc(sizeof(reg_cache_t)); + reg_t *reg_list = NULL; + etm_reg_t *arch_info = NULL; + int num_regs = sizeof(etm_reg_arch_info)/sizeof(int); + int i; + + /* register a register arch-type for etm registers only once */ + if (etm_reg_arch_type == -1) + etm_reg_arch_type = register_reg_arch_type(etm_get_reg, etm_set_reg_w_exec); + + /* the actual registers are kept in two arrays */ + reg_list = calloc(num_regs, sizeof(reg_t)); + arch_info = calloc(num_regs, sizeof(etm_reg_t)); + + /* fill in values for the reg cache */ + reg_cache->name = "etm registers"; + reg_cache->next = NULL; + reg_cache->reg_list = reg_list; + reg_cache->num_regs = num_regs; + + /* set up registers */ + for (i = 0; i < num_regs; i++) + { + reg_list[i].name = etm_reg_list[i]; + reg_list[i].size = 32; + reg_list[i].dirty = 0; + reg_list[i].valid = 0; + reg_list[i].bitfield_desc = NULL; + reg_list[i].num_bitfields = 0; + reg_list[i].value = calloc(1, 4); + reg_list[i].arch_info = &arch_info[i]; + reg_list[i].arch_type = etm_reg_arch_type; + reg_list[i].size = etm_reg_arch_size_info[i]; + arch_info[i].addr = etm_reg_arch_info[i]; + arch_info[i].jtag_info = jtag_info; + } + return reg_cache; +} + +int etm_get_reg(reg_t *reg) +{ + if (etm_read_reg(reg) != ERROR_OK) + { + ERROR("BUG: error scheduling etm register read"); + exit(-1); + } + + if (jtag_execute_queue() != ERROR_OK) + { + ERROR("register read failed"); + } + + return ERROR_OK; +} + +int etm_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask) +{ + etm_reg_t *etm_reg = reg->arch_info; + u8 reg_addr = etm_reg->addr & 0x7f; + scan_field_t fields[3]; + + DEBUG("%i", etm_reg->addr); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(etm_reg->jtag_info, 0x6); + arm_jtag_set_instr(etm_reg->jtag_info, etm_reg->jtag_info->intest_instr); + + fields[0].device = etm_reg->jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = reg->value; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = etm_reg->jtag_info->chain_pos; + fields[1].num_bits = 7; + fields[1].out_value = malloc(1); + buf_set_u32(fields[1].out_value, 0, 7, reg_addr); + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = etm_reg->jtag_info->chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = malloc(1); + buf_set_u32(fields[2].out_value, 0, 1, 0); + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + fields[0].in_value = reg->value; + fields[0].in_check_value = check_value; + fields[0].in_check_mask = check_mask; + + jtag_add_dr_scan(3, fields, -1); + + free(fields[1].out_value); + free(fields[2].out_value); + + return ERROR_OK; +} + +int etm_read_reg(reg_t *reg) +{ + return etm_read_reg_w_check(reg, NULL, NULL); +} + +int etm_set_reg(reg_t *reg, u32 value) +{ + if (etm_write_reg(reg, value) != ERROR_OK) + { + ERROR("BUG: error scheduling etm register write"); + exit(-1); + } + + buf_set_u32(reg->value, 0, reg->size, value); + reg->valid = 1; + reg->dirty = 0; + + return ERROR_OK; +} + +int etm_set_reg_w_exec(reg_t *reg, u32 value) +{ + etm_set_reg(reg, value); + + if (jtag_execute_queue() != ERROR_OK) + { + ERROR("register write failed"); + exit(-1); + } + return ERROR_OK; +} + +int etm_write_reg(reg_t *reg, u32 value) +{ + etm_reg_t *etm_reg = reg->arch_info; + u8 reg_addr = etm_reg->addr & 0x7f; + scan_field_t fields[3]; + + DEBUG("%i: 0x%8.8x", etm_reg->addr, value); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(etm_reg->jtag_info, 0x6); + arm_jtag_set_instr(etm_reg->jtag_info, etm_reg->jtag_info->intest_instr); + + fields[0].device = etm_reg->jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = malloc(4); + buf_set_u32(fields[0].out_value, 0, 32, value); + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = etm_reg->jtag_info->chain_pos; + fields[1].num_bits = 7; + fields[1].out_value = malloc(1); + buf_set_u32(fields[1].out_value, 0, 7, reg_addr); + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = etm_reg->jtag_info->chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = malloc(1); + buf_set_u32(fields[2].out_value, 0, 1, 1); + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + free(fields[0].out_value); + free(fields[1].out_value); + free(fields[2].out_value); + + return ERROR_OK; +} + +int etm_store_reg(reg_t *reg) +{ + return etm_write_reg(reg, buf_get_u32(reg->value, 0, reg->size)); +} + diff --git a/src/target/etm.h b/src/target/etm.h new file mode 100644 index 00000000..dbe78f35 --- /dev/null +++ b/src/target/etm.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef ETM_H +#define ETM_H + +#include "target.h" +#include "register.h" +#include "arm_jtag.h" + +// ETM registers (V1.2 protocol) +enum +{ + ETM_CTRL = 0x00, + ETM_CONFIG = 0x01, + ETM_TRIG_EVENT = 0x02, + ETM_MMD_CTRL = 0x03, + ETM_STATUS = 0x04, + ETM_SYS_CONFIG = 0x05, + ETM_TRACE_RESOURCE_CTRL = 0x06, + ETM_TRACE_EN_CTRL2 = 0x07, + ETM_TRACE_EN_EVENT = 0x08, + ETM_TRACE_EN_CTRL1 = 0x09, + ETM_FIFOFULL_REGION = 0x0a, + ETM_FIFOFULL_LEVEL = 0x0b, + ETM_VIEWDATA_EVENT = 0x0c, + ETM_VIEWDATA_CTRL1 = 0x0d, + ETM_VIEWDATA_CTRL2 = 0x0e, + ETM_VIEWDATA_CTRL3 = 0x0f, + ETM_ADDR_COMPARATOR_VALUE = 0x10, + ETM_ADDR_ACCESS_TYPE = 0x20, + ETM_DATA_COMPARATOR_VALUE = 0x30, + ETM_DATA_COMPARATOR_MASK = 0x40, + ETM_COUNTER_INITAL_VALUE = 0x50, + ETM_COUNTER_ENABLE = 0x54, + ETM_COUNTER_RELOAD_VALUE = 0x58, + ETM_COUNTER_VALUE = 0x5c, + ETM_SEQUENCER_CTRL = 0x60, + ETM_SEQUENCER_STATE = 0x67, + ETM_EXTERNAL_OUTPUT = 0x68, + ETM_CONTEXTID_COMPARATOR_VALUE = 0x6c, + ETM_CONTEXTID_COMPARATOR_MASK = 0x6f, +}; + + +typedef struct etm_reg_s +{ + int addr; + arm_jtag_t *jtag_info; +} etm_reg_t; + +extern reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg); +extern int etm_read_reg(reg_t *reg); +extern int etm_write_reg(reg_t *reg, u32 value); +extern int etm_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask); +extern int etm_store_reg(reg_t *reg); +extern int etm_set_reg(reg_t *reg, u32 value); +extern int etm_set_reg_w_exec(reg_t *reg, u32 value); + +#endif /* ETM_H */ diff --git a/src/target/register.c b/src/target/register.c new file mode 100644 index 00000000..7b98cfcf --- /dev/null +++ b/src/target/register.c @@ -0,0 +1,100 @@ +/*************************************************************************** + * 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 "register.h" + +#include "log.h" +#include "command.h" + +#include +#include + +reg_arch_type_t *reg_arch_types = NULL; + +reg_t* register_get_by_name(reg_cache_t *first, char *name, int search_all) +{ + int i; + reg_cache_t *cache = first; + + while (cache) + { + for (i = 0; i < cache->num_regs; i++) + { + if (strcmp(cache->reg_list[i].name, name) == 0) + return &(cache->reg_list[i]); + } + + if (search_all) + cache = cache->next; + else + break; + } + + return NULL; +} + +reg_cache_t** register_get_last_cache_p(reg_cache_t **first) +{ + reg_cache_t **cache_p = first; + + if (*cache_p) + while (*cache_p) + cache_p = &((*cache_p)->next); + else + return first; + + return cache_p; +} + +int register_reg_arch_type(int (*get)(reg_t *reg), int (*set)(reg_t *reg, u32 value)) +{ + reg_arch_type_t** arch_type_p = ®_arch_types; + int id = 0; + + if (*arch_type_p) + { + while (*arch_type_p) + { + id = (*arch_type_p)->id; + arch_type_p = &((*arch_type_p)->next); + } + } + + (*arch_type_p) = malloc(sizeof(reg_arch_type_t)); + (*arch_type_p)->id = id + 1; + (*arch_type_p)->set = set; + (*arch_type_p)->get = get; + (*arch_type_p)->next = NULL; + + return id + 1; +} + +reg_arch_type_t* register_get_arch_type(int id) +{ + reg_arch_type_t *arch_type = reg_arch_types; + + while (arch_type) + { + if (arch_type->id == id) + return arch_type; + arch_type = arch_type->next; + } + + return NULL; +} diff --git a/src/target/register.h b/src/target/register.h new file mode 100644 index 00000000..8a903edd --- /dev/null +++ b/src/target/register.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef REGISTER_H +#define REGISTER_H + +#include "types.h" +#include "target.h" + +struct target_s; + +typedef struct bitfield_desc_s +{ + char *name; + int num_bits; +} bitfield_desc_t; + +typedef struct reg_s +{ + char *name; + u8 *value; + int dirty; + int valid; + int size; + bitfield_desc_t *bitfield_desc; + int num_bitfields; + void *arch_info; + int arch_type; +} reg_t; + +typedef struct reg_cache_s +{ + char *name; + struct reg_cache_s *next; + reg_t *reg_list; + int num_regs; +} reg_cache_t; + +typedef struct reg_arch_type_s +{ + int id; + int (*get)(reg_t *reg); + int (*set)(reg_t *reg, u32 value); + struct reg_arch_type_s *next; +} reg_arch_type_t; + +extern reg_t* register_get_by_name(reg_cache_t *first, char *name, int search_all); +extern reg_cache_t** register_get_last_cache_p(reg_cache_t **first); +extern int register_reg_arch_type(int (*get)(reg_t *reg), int (*set)(reg_t *reg, u32 value)); +extern reg_arch_type_t* register_get_arch_type(int id); + +#endif /* REGISTER_H */ + 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; +} + diff --git a/src/target/target.h b/src/target/target.h new file mode 100644 index 00000000..6d3b6d17 --- /dev/null +++ b/src/target/target.h @@ -0,0 +1,231 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef TARGET_H +#define TARGET_H + +#include "register.h" +#include "breakpoints.h" +#include "algorithm.h" + +#include "command.h" +#include "types.h" + +#include +#include + +struct reg_s; +struct command_context_s; + +enum target_state +{ + TARGET_UNKNOWN = 0, + TARGET_RUNNING = 1, + TARGET_HALTED = 2, + TARGET_RESET = 3, + TARGET_DEBUG_RUNNING = 4, +}; + +extern char *target_state_strings[]; + +enum daemon_startup_mode +{ + DAEMON_ATTACH, /* simply attach to the target */ + DAEMON_RESET, /* reset target (behaviour defined by reset_mode */ +}; + +enum target_reset_mode +{ + RESET_RUN = 0, /* reset and let target run */ + RESET_HALT = 1, /* reset and halt target out of reset */ + RESET_INIT = 2, /* reset and halt target out of reset, then run init script */ + RESET_RUN_AND_HALT = 3, /* reset and let target run, halt after n milliseconds */ + RESET_RUN_AND_INIT = 4, /* reset and let target run, halt after n milliseconds, then run init script */ +}; + +enum target_debug_reason +{ + DBG_REASON_DBGRQ = 0, + DBG_REASON_BREAKPOINT = 1, + DBG_REASON_WATCHPOINT = 2, + DBG_REASON_WPTANDBKPT = 3, + DBG_REASON_SINGLESTEP = 4, + DBG_REASON_NOTHALTED = 5 +}; + +extern char *target_debug_reason_strings[]; + +enum target_endianess +{ + TARGET_BIG_ENDIAN = 0, TARGET_LITTLE_ENDIAN = 1 +}; + +extern char *target_endianess_strings[]; + +struct target_s; + +typedef struct working_area_s +{ + u32 address; + u32 size; + int free; + u8 *backup; + struct working_area_s **user; + struct working_area_s *next; +} working_area_t; + +typedef struct target_type_s +{ + char *name; + + /* poll current target status */ + enum target_state (*poll)(struct target_s *target); + /* architecture specific status reply */ + int (*arch_state)(struct target_s *target, char *buf, int buf_size); + + /* target execution control */ + int (*halt)(struct target_s *target); + int (*resume)(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution); + int (*step)(struct target_s *target, int current, u32 address, int handle_breakpoints); + + /* target reset control */ + int (*assert_reset)(struct target_s *target); + int (*deassert_reset)(struct target_s *target); + int (*soft_reset_halt)(struct target_s *target); + + /* target register access for gdb */ + int (*get_gdb_reg_list)(struct target_s *target, struct reg_s **reg_list[], int *reg_list_size); + + /* target memory access + * size: 1 = byte (8bit), 2 = half-word (16bit), 4 = word (32bit) + * count: number of items of + */ + int (*read_memory)(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + int (*write_memory)(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + + /* write target memory in multiples of 4 byte, optimized for writing large quantities of data */ + int (*bulk_write_memory)(struct target_s *target, u32 address, u32 count, u8 *buffer); + + /* target break-/watchpoint control + * rw: 0 = write, 1 = read, 2 = access + */ + int (*add_breakpoint)(struct target_s *target, u32 address, u32 length, enum breakpoint_type type); + int (*remove_breakpoint)(struct target_s *target, breakpoint_t *breakpoint); + int (*add_watchpoint)(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw); + int (*remove_watchpoint)(struct target_s *target, watchpoint_t *watchpoint); + + /* target algorithm support */ + int (*run_algorithm)(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_params, reg_param_t *reg_param, u32 entry_point, u32 exit_point, int timeout_ms, void *arch_info); + + int (*register_commands)(struct command_context_s *cmd_ctx); + int (*target_command)(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); + int (*init_target)(struct command_context_s *cmd_ctx, struct target_s *target); + int (*quit)(void); + +} target_type_t; + +typedef struct target_s +{ + target_type_t *type; /* target type definition (name, access functions) */ + enum target_reset_mode reset_mode; /* what to do after a reset */ + int run_and_halt_time; /* how long the target should run after a run_and_halt reset */ + char *reset_script; /* script file to initialize the target after a reset */ + char *post_halt_script; /* script file to execute after the target halted */ + char *pre_resume_script; /* script file to execute before the target resumed */ + u32 working_area; /* working area (initialized RAM) */ + u32 working_area_size; /* size in bytes */ + u32 backup_working_area; /* whether the content of the working area has to be preserved */ + struct working_area_s *working_areas;/* list of allocated working areas */ + enum target_debug_reason debug_reason; /* reason why the target entered debug state */ + enum target_endianess endianness; /* target endianess */ + enum target_state state; /* the current backend-state (running, halted, ...) */ + struct reg_cache_s *reg_cache; /* the first register cache of the target (core regs) */ + struct breakpoint_s *breakpoints; /* list of breakpoints */ + struct watchpoint_s *watchpoints; /* list of watchpoints */ + void *arch_info; /* architecture specific information */ + struct target_s *next; /* next target in list */ +} target_t; + +enum target_event +{ + TARGET_EVENT_HALTED, /* target entered debug state from normal execution or reset */ + TARGET_EVENT_RESUMED, /* target resumed to normal execution */ + TARGET_EVENT_RESET, /* target entered reset */ + TARGET_EVENT_DEBUG_HALTED, /* target entered debug state, but was executing on behalf of the debugger */ + TARGET_EVENT_DEBUG_RESUMED, /* target resumed to execute on behalf of the debugger */ +}; + +typedef struct target_event_callback_s +{ + int (*callback)(struct target_s *target, enum target_event event, void *priv); + void *priv; + struct target_event_callback_s *next; +} target_event_callback_t; + +typedef struct target_timer_callback_s +{ + int (*callback)(void *priv); + int time_ms; + int periodic; + struct timeval when; + void *priv; + struct target_timer_callback_s *next; +} target_timer_callback_t; + +extern int target_register_commands(struct command_context_s *cmd_ctx); +extern int target_register_user_commands(struct command_context_s *cmd_ctx); +extern int target_init(struct command_context_s *cmd_ctx); +extern int handle_target(void *priv); + +extern int target_register_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv); +extern int target_unregister_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv); +extern int target_call_event_callbacks(target_t *target, enum target_event event); + +extern int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv); +extern int target_unregister_timer_callback(int (*callback)(void *priv), void *priv); +extern int target_call_timer_callbacks(); + +extern target_t* get_current_target(struct command_context_s *cmd_ctx); +extern int get_num_by_target(target_t *query_target); +extern target_t* get_target_by_num(int num); + +extern int target_write_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer); +extern int target_read_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer); + +extern int target_alloc_working_area(struct target_s *target, u32 size, working_area_t **area); +extern int target_free_working_area(struct target_s *target, working_area_t *area); +extern int target_free_all_working_areas(struct target_s *target); + +extern target_t *targets; + +extern target_event_callback_t *target_event_callbacks; +extern target_timer_callback_t *target_timer_callbacks; + +#define ERROR_TARGET_INVALID (-300) +#define ERROR_TARGET_INIT_FAILED (-301) +#define ERROR_TARGET_TIMEOUT (-302) +#define ERROR_TARGET_ALREADY_HALTED (-303) +#define ERROR_TARGET_NOT_HALTED (-304) +#define ERROR_TARGET_FAILURE (-305) +#define ERROR_TARGET_UNALIGNED_ACCESS (-306) +#define ERROR_TARGET_DATA_ABORT (-307) +#define ERROR_TARGET_RESOURCE_NOT_AVAILABLE (-308) +#define ERROR_TARGET_TRANSLATION_FAULT (-309) + +#endif /* TARGET_H */ -- cgit v1.2.3