forked from auracaster/openocd
aa7c449600
Most files in the tree seem to have ended up including this, and *quite* needlessly ... only code implementing or using breakpoints actually needs these declarations. So take it out of the header files which included it, and put it in files which use it ... reduce needless interdependencies. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
891 lines
26 KiB
C
891 lines
26 KiB
C
/***************************************************************************
|
|
* Copyright (C) 2005 by Dominic Rath *
|
|
* Dominic.Rath@gmx.de *
|
|
* *
|
|
* Copyright (C) 2008 by Spencer Oliver *
|
|
* spen@spen-soft.co.uk *
|
|
* *
|
|
* Copyright (C) 2008 by Oyvind Harboe *
|
|
* oyvind.harboe@zylin.com *
|
|
* *
|
|
* 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. *
|
|
***************************************************************************/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "armv4_5.h"
|
|
#include "breakpoints.h"
|
|
#include "arm_disassembler.h"
|
|
#include "binarybuffer.h"
|
|
|
|
|
|
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_list[] =
|
|
{
|
|
"Illegal mode value", "User", "FIQ", "IRQ", "Supervisor", "Abort", "Undefined", "System"
|
|
};
|
|
|
|
/* Hack! Yuk! allow -1 index, which simplifies codepaths elsewhere in the code */
|
|
char** armv4_5_mode_strings = armv4_5_mode_strings_list + 1;
|
|
|
|
char* armv4_5_state_strings[] =
|
|
{
|
|
"ARM", "Thumb", "Jazelle"
|
|
};
|
|
|
|
int armv4_5_core_reg_arch_type = -1;
|
|
|
|
struct armv4_5_core_reg 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
|
|
}
|
|
};
|
|
|
|
uint8_t armv4_5_gdb_dummy_fp_value[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
struct reg armv4_5_gdb_dummy_fp_reg =
|
|
{
|
|
.name = "GDB dummy floating-point register",
|
|
.value = armv4_5_gdb_dummy_fp_value,
|
|
.dirty = 0,
|
|
.valid = 1,
|
|
.size = 96,
|
|
.arch_info = NULL,
|
|
.arch_type = 0,
|
|
};
|
|
|
|
uint8_t armv4_5_gdb_dummy_fps_value[] = {0, 0, 0, 0};
|
|
|
|
struct reg armv4_5_gdb_dummy_fps_reg =
|
|
{
|
|
.name = "GDB dummy floating-point status register",
|
|
.value = armv4_5_gdb_dummy_fps_value,
|
|
.dirty = 0,
|
|
.valid = 1,
|
|
.size = 32,
|
|
.arch_info = NULL,
|
|
.arch_type = 0,
|
|
};
|
|
|
|
int armv4_5_get_core_reg(struct reg *reg)
|
|
{
|
|
int retval;
|
|
struct armv4_5_core_reg *armv4_5 = reg->arch_info;
|
|
struct target *target = armv4_5->target;
|
|
|
|
if (target->state != TARGET_HALTED)
|
|
{
|
|
LOG_ERROR("Target not 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(struct reg *reg, uint8_t *buf)
|
|
{
|
|
struct armv4_5_core_reg *armv4_5 = reg->arch_info;
|
|
struct target *target = armv4_5->target;
|
|
struct armv4_5_common_s *armv4_5_target = target_to_armv4_5(target);
|
|
uint32_t value = buf_get_u32(buf, 0, 32);
|
|
|
|
if (target->state != TARGET_HALTED)
|
|
{
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
}
|
|
|
|
if (reg == &armv4_5_target->core_cache->reg_list[ARMV4_5_CPSR])
|
|
{
|
|
if (value & 0x20)
|
|
{
|
|
/* T bit should be set */
|
|
if (armv4_5_target->core_state == ARMV4_5_STATE_ARM)
|
|
{
|
|
/* change state to Thumb */
|
|
LOG_DEBUG("changing to Thumb state");
|
|
armv4_5_target->core_state = ARMV4_5_STATE_THUMB;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* T bit should be cleared */
|
|
if (armv4_5_target->core_state == ARMV4_5_STATE_THUMB)
|
|
{
|
|
/* change state to ARM */
|
|
LOG_DEBUG("changing to ARM state");
|
|
armv4_5_target->core_state = ARMV4_5_STATE_ARM;
|
|
}
|
|
}
|
|
|
|
if (armv4_5_target->core_mode != (enum armv4_5_mode)(value & 0x1f))
|
|
{
|
|
LOG_DEBUG("changing ARM core mode to '%s'", armv4_5_mode_strings[armv4_5_mode_to_number(value & 0x1f)]);
|
|
armv4_5_target->core_mode = value & 0x1f;
|
|
armv4_5_target->write_core_reg(target, 16, ARMV4_5_MODE_ANY, value);
|
|
}
|
|
}
|
|
|
|
buf_set_u32(reg->value, 0, 32, value);
|
|
reg->dirty = 1;
|
|
reg->valid = 1;
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
int armv4_5_invalidate_core_regs(struct target *target)
|
|
{
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
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;
|
|
}
|
|
|
|
struct reg_cache* armv4_5_build_reg_cache(struct target *target, struct arm *armv4_5_common)
|
|
{
|
|
int num_regs = 37;
|
|
struct reg_cache *cache = malloc(sizeof(struct reg_cache));
|
|
struct reg *reg_list = malloc(sizeof(struct reg) * num_regs);
|
|
struct armv4_5_core_reg *arch_info = malloc(sizeof(struct armv4_5_core_reg) * 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);
|
|
|
|
register_init_dummy(&armv4_5_gdb_dummy_fp_reg);
|
|
register_init_dummy(&armv4_5_gdb_dummy_fps_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].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 *target)
|
|
{
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
|
|
if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC)
|
|
{
|
|
LOG_ERROR("BUG: called for a non-ARMv4/5 target");
|
|
exit(-1);
|
|
}
|
|
|
|
LOG_USER("target halted in %s state due to %s, current mode: %s\ncpsr: 0x%8.8" PRIx32 " pc: 0x%8.8" PRIx32 "",
|
|
armv4_5_state_strings[armv4_5->core_state],
|
|
Jim_Nvp_value2name_simple(nvp_target_debug_reason, target->debug_reason)->name,
|
|
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;
|
|
}
|
|
|
|
COMMAND_HANDLER(handle_armv4_5_reg_command)
|
|
{
|
|
char output[128];
|
|
int output_len;
|
|
int mode, num;
|
|
struct target *target = get_current_target(cmd_ctx);
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
|
|
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;
|
|
}
|
|
|
|
if (armv4_5_mode_to_number(armv4_5->core_mode)==-1)
|
|
return ERROR_FAIL;
|
|
|
|
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.8" PRIx32 " ",
|
|
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, "%s", output);
|
|
}
|
|
command_print(cmd_ctx,
|
|
" cpsr: %8.8" PRIx32 " spsr_fiq: %8.8" PRIx32 " spsr_irq: %8.8" PRIx32 " spsr_svc: %8.8" PRIx32 " spsr_abt: %8.8" PRIx32 " spsr_und: %8.8" PRIx32 "",
|
|
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;
|
|
}
|
|
|
|
COMMAND_HANDLER(handle_armv4_5_core_state_command)
|
|
{
|
|
struct target *target = get_current_target(cmd_ctx);
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
|
|
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;
|
|
}
|
|
|
|
COMMAND_HANDLER(handle_armv4_5_disassemble_command)
|
|
{
|
|
int retval = ERROR_OK;
|
|
struct target *target = get_current_target(cmd_ctx);
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
uint32_t address;
|
|
int count = 1;
|
|
int i;
|
|
struct arm_instruction cur_instruction;
|
|
uint32_t opcode;
|
|
uint16_t thumb_opcode;
|
|
int thumb = 0;
|
|
|
|
if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC)
|
|
{
|
|
command_print(cmd_ctx, "current target isn't an ARMV4/5 target");
|
|
return ERROR_OK;
|
|
}
|
|
|
|
switch (argc) {
|
|
case 3:
|
|
if (strcmp(args[2], "thumb") != 0)
|
|
goto usage;
|
|
thumb = 1;
|
|
/* FALL THROUGH */
|
|
case 2:
|
|
COMMAND_PARSE_NUMBER(int, args[1], count);
|
|
/* FALL THROUGH */
|
|
case 1:
|
|
COMMAND_PARSE_NUMBER(u32, args[0], address);
|
|
if (address & 0x01) {
|
|
if (!thumb) {
|
|
command_print(cmd_ctx, "Disassemble as Thumb");
|
|
thumb = 1;
|
|
}
|
|
address &= ~1;
|
|
}
|
|
break;
|
|
default:
|
|
usage:
|
|
command_print(cmd_ctx,
|
|
"usage: armv4_5 disassemble <address> [<count> ['thumb']]");
|
|
return ERROR_OK;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (thumb)
|
|
{
|
|
if ((retval = target_read_u16(target, address, &thumb_opcode)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
if ((retval = thumb_evaluate_opcode(thumb_opcode, address, &cur_instruction)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
}
|
|
else {
|
|
if ((retval = target_read_u32(target, address, &opcode)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
if ((retval = arm_evaluate_opcode(opcode, address, &cur_instruction)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
}
|
|
command_print(cmd_ctx, "%s", cur_instruction.text);
|
|
address += (thumb) ? 2 : 4;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
int armv4_5_register_commands(struct command_context *cmd_ctx)
|
|
{
|
|
struct command *armv4_5_cmd;
|
|
|
|
armv4_5_cmd = register_command(cmd_ctx, NULL, "armv4_5",
|
|
NULL, COMMAND_ANY,
|
|
"armv4/5 specific commands");
|
|
|
|
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 <arm | thumb>");
|
|
register_command(cmd_ctx, armv4_5_cmd, "disassemble",
|
|
handle_armv4_5_disassemble_command, COMMAND_EXEC,
|
|
"disassemble instructions <address> [<count> ['thumb']]");
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
int armv4_5_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size)
|
|
{
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
int i;
|
|
|
|
if (armv4_5_mode_to_number(armv4_5->core_mode)==-1)
|
|
return ERROR_FAIL;
|
|
|
|
*reg_list_size = 26;
|
|
*reg_list = malloc(sizeof(struct reg*) * (*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;
|
|
}
|
|
|
|
/* wait for execution to complete and check exit point */
|
|
static int armv4_5_run_algorithm_completion(struct target *target, uint32_t exit_point, int timeout_ms, void *arch_info)
|
|
{
|
|
int retval;
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
|
|
if ((retval = target_wait_state(target, TARGET_HALTED, timeout_ms)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
if (target->state != TARGET_HALTED)
|
|
{
|
|
if ((retval = target_halt(target)) != ERROR_OK)
|
|
return retval;
|
|
if ((retval = target_wait_state(target, TARGET_HALTED, 500)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
return ERROR_TARGET_TIMEOUT;
|
|
}
|
|
|
|
/* fast exit: ARMv5+ code can use BKPT */
|
|
if (exit_point && buf_get_u32(armv4_5->core_cache->reg_list[15].value,
|
|
0, 32) != exit_point)
|
|
{
|
|
LOG_WARNING("target reentered debug state, but not at the desired exit point: 0x%4.4" PRIx32 "",
|
|
buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32));
|
|
return ERROR_TARGET_TIMEOUT;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
int armv4_5_run_algorithm_inner(struct target *target, int num_mem_params, struct mem_param *mem_params, int num_reg_params, struct reg_param *reg_params, uint32_t entry_point, uint32_t exit_point, int timeout_ms, void *arch_info, int (*run_it)(struct target *target, uint32_t exit_point, int timeout_ms, void *arch_info))
|
|
{
|
|
struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target);
|
|
struct armv4_5_algorithm *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;
|
|
uint32_t context[17];
|
|
uint32_t cpsr;
|
|
int exit_breakpoint_size = 0;
|
|
int i;
|
|
int retval = ERROR_OK;
|
|
LOG_DEBUG("Running algorithm");
|
|
|
|
if (armv4_5_algorithm_info->common_magic != ARMV4_5_COMMON_MAGIC)
|
|
{
|
|
LOG_ERROR("current target isn't an ARMV4/5 target");
|
|
return ERROR_TARGET_INVALID;
|
|
}
|
|
|
|
if (target->state != TARGET_HALTED)
|
|
{
|
|
LOG_WARNING("target not halted");
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
}
|
|
|
|
if (armv4_5_mode_to_number(armv4_5->core_mode)==-1)
|
|
return ERROR_FAIL;
|
|
|
|
/* armv5 and later can terminate with BKPT instruction; less overhead */
|
|
if (!exit_point && armv4_5->is_armv4)
|
|
{
|
|
LOG_ERROR("ARMv4 target needs HW breakpoint location");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
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++)
|
|
{
|
|
if ((retval = target_write_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_reg_params; i++)
|
|
{
|
|
struct reg *reg = register_get_by_name(armv4_5->core_cache, reg_params[i].reg_name, 0);
|
|
if (!reg)
|
|
{
|
|
LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
|
|
exit(-1);
|
|
}
|
|
|
|
if (reg->size != reg_params[i].size)
|
|
{
|
|
LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name);
|
|
exit(-1);
|
|
}
|
|
|
|
if ((retval = armv4_5_set_core_reg(reg, reg_params[i].value)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
LOG_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)
|
|
{
|
|
LOG_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;
|
|
}
|
|
|
|
/* terminate using a hardware or (ARMv5+) software breakpoint */
|
|
if (exit_point && (retval = breakpoint_add(target, exit_point,
|
|
exit_breakpoint_size, BKPT_HARD)) != ERROR_OK)
|
|
{
|
|
LOG_ERROR("can't add HW breakpoint to terminate algorithm");
|
|
return ERROR_TARGET_FAILURE;
|
|
}
|
|
|
|
if ((retval = target_resume(target, 0, entry_point, 1, 1)) != ERROR_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
int retvaltemp;
|
|
retval = run_it(target, exit_point, timeout_ms, arch_info);
|
|
|
|
if (exit_point)
|
|
breakpoint_remove(target, exit_point);
|
|
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
for (i = 0; i < num_mem_params; i++)
|
|
{
|
|
if (mem_params[i].direction != PARAM_OUT)
|
|
if ((retvaltemp = target_read_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value)) != ERROR_OK)
|
|
{
|
|
retval = retvaltemp;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_reg_params; i++)
|
|
{
|
|
if (reg_params[i].direction != PARAM_OUT)
|
|
{
|
|
|
|
struct reg *reg = register_get_by_name(armv4_5->core_cache, reg_params[i].reg_name, 0);
|
|
if (!reg)
|
|
{
|
|
LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
|
|
exit(-1);
|
|
}
|
|
|
|
if (reg->size != reg_params[i].size)
|
|
{
|
|
LOG_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++)
|
|
{
|
|
uint32_t regvalue;
|
|
regvalue = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).value, 0, 32);
|
|
if (regvalue != context[i])
|
|
{
|
|
LOG_DEBUG("restoring register %s with value 0x%8.8" PRIx32 "", ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).name, context[i]);
|
|
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_run_algorithm(struct target *target, int num_mem_params, struct mem_param *mem_params, int num_reg_params, struct reg_param *reg_params, uint32_t entry_point, uint32_t exit_point, int timeout_ms, void *arch_info)
|
|
{
|
|
return armv4_5_run_algorithm_inner(target, num_mem_params, mem_params, num_reg_params, reg_params, entry_point, exit_point, timeout_ms, arch_info, armv4_5_run_algorithm_completion);
|
|
}
|
|
|
|
/**
|
|
* Runs ARM code in the target to calculate a CRC32 checksum.
|
|
*
|
|
* \todo On ARMv5+, rely on BKPT termination for reduced overhead.
|
|
*/
|
|
int arm_checksum_memory(struct target *target,
|
|
uint32_t address, uint32_t count, uint32_t *checksum)
|
|
{
|
|
struct working_area *crc_algorithm;
|
|
struct armv4_5_algorithm armv4_5_info;
|
|
struct reg_param reg_params[2];
|
|
int retval;
|
|
uint32_t i;
|
|
|
|
static const uint32_t arm_crc_code[] = {
|
|
0xE1A02000, /* mov r2, r0 */
|
|
0xE3E00000, /* mov r0, #0xffffffff */
|
|
0xE1A03001, /* mov r3, r1 */
|
|
0xE3A04000, /* mov r4, #0 */
|
|
0xEA00000B, /* b ncomp */
|
|
/* nbyte: */
|
|
0xE7D21004, /* ldrb r1, [r2, r4] */
|
|
0xE59F7030, /* ldr r7, CRC32XOR */
|
|
0xE0200C01, /* eor r0, r0, r1, asl 24 */
|
|
0xE3A05000, /* mov r5, #0 */
|
|
/* loop: */
|
|
0xE3500000, /* cmp r0, #0 */
|
|
0xE1A06080, /* mov r6, r0, asl #1 */
|
|
0xE2855001, /* add r5, r5, #1 */
|
|
0xE1A00006, /* mov r0, r6 */
|
|
0xB0260007, /* eorlt r0, r6, r7 */
|
|
0xE3550008, /* cmp r5, #8 */
|
|
0x1AFFFFF8, /* bne loop */
|
|
0xE2844001, /* add r4, r4, #1 */
|
|
/* ncomp: */
|
|
0xE1540003, /* cmp r4, r3 */
|
|
0x1AFFFFF1, /* bne nbyte */
|
|
/* end: */
|
|
0xEAFFFFFE, /* b end */
|
|
/* CRC32XOR: */
|
|
0x04C11DB7 /* .word 0x04C11DB7 */
|
|
};
|
|
|
|
retval = target_alloc_working_area(target,
|
|
sizeof(arm_crc_code), &crc_algorithm);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
/* convert code into a buffer in target endianness */
|
|
for (i = 0; i < DIM(arm_crc_code); i++) {
|
|
retval = target_write_u32(target,
|
|
crc_algorithm->address + i * sizeof(uint32_t),
|
|
arm_crc_code[i]);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
}
|
|
|
|
armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;
|
|
armv4_5_info.core_mode = ARMV4_5_MODE_SVC;
|
|
armv4_5_info.core_state = ARMV4_5_STATE_ARM;
|
|
|
|
init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT);
|
|
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
|
|
|
buf_set_u32(reg_params[0].value, 0, 32, address);
|
|
buf_set_u32(reg_params[1].value, 0, 32, count);
|
|
|
|
/* 20 second timeout/megabyte */
|
|
int timeout = 20000 * (1 + (count / (1024 * 1024)));
|
|
|
|
retval = target_run_algorithm(target, 0, NULL, 2, reg_params,
|
|
crc_algorithm->address,
|
|
crc_algorithm->address + sizeof(arm_crc_code) - 8,
|
|
timeout, &armv4_5_info);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("error executing ARM crc algorithm");
|
|
destroy_reg_param(®_params[0]);
|
|
destroy_reg_param(®_params[1]);
|
|
target_free_working_area(target, crc_algorithm);
|
|
return retval;
|
|
}
|
|
|
|
*checksum = buf_get_u32(reg_params[0].value, 0, 32);
|
|
|
|
destroy_reg_param(®_params[0]);
|
|
destroy_reg_param(®_params[1]);
|
|
|
|
target_free_working_area(target, crc_algorithm);
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
/**
|
|
* Runs ARM code in the target to check whether a memory block holds
|
|
* all ones. NOR flash which has been erased, and thus may be written,
|
|
* holds all ones.
|
|
*
|
|
* \todo On ARMv5+, rely on BKPT termination for reduced overhead.
|
|
*/
|
|
int arm_blank_check_memory(struct target *target,
|
|
uint32_t address, uint32_t count, uint32_t *blank)
|
|
{
|
|
struct working_area *check_algorithm;
|
|
struct reg_param reg_params[3];
|
|
struct armv4_5_algorithm armv4_5_info;
|
|
int retval;
|
|
uint32_t i;
|
|
|
|
static const uint32_t check_code[] = {
|
|
/* loop: */
|
|
0xe4d03001, /* ldrb r3, [r0], #1 */
|
|
0xe0022003, /* and r2, r2, r3 */
|
|
0xe2511001, /* subs r1, r1, #1 */
|
|
0x1afffffb, /* bne loop */
|
|
/* end: */
|
|
0xeafffffe /* b end */
|
|
};
|
|
|
|
/* make sure we have a working area */
|
|
retval = target_alloc_working_area(target,
|
|
sizeof(check_code), &check_algorithm);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
/* convert code into a buffer in target endianness */
|
|
for (i = 0; i < DIM(check_code); i++) {
|
|
retval = target_write_u32(target,
|
|
check_algorithm->address
|
|
+ i * sizeof(uint32_t),
|
|
check_code[i]);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
}
|
|
|
|
armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;
|
|
armv4_5_info.core_mode = ARMV4_5_MODE_SVC;
|
|
armv4_5_info.core_state = ARMV4_5_STATE_ARM;
|
|
|
|
init_reg_param(®_params[0], "r0", 32, PARAM_OUT);
|
|
buf_set_u32(reg_params[0].value, 0, 32, address);
|
|
|
|
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
|
buf_set_u32(reg_params[1].value, 0, 32, count);
|
|
|
|
init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT);
|
|
buf_set_u32(reg_params[2].value, 0, 32, 0xff);
|
|
|
|
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
|
check_algorithm->address,
|
|
check_algorithm->address + sizeof(check_code) - 4,
|
|
10000, &armv4_5_info);
|
|
if (retval != ERROR_OK) {
|
|
destroy_reg_param(®_params[0]);
|
|
destroy_reg_param(®_params[1]);
|
|
destroy_reg_param(®_params[2]);
|
|
target_free_working_area(target, check_algorithm);
|
|
return retval;
|
|
}
|
|
|
|
*blank = buf_get_u32(reg_params[2].value, 0, 32);
|
|
|
|
destroy_reg_param(®_params[0]);
|
|
destroy_reg_param(®_params[1]);
|
|
destroy_reg_param(®_params[2]);
|
|
|
|
target_free_working_area(target, check_algorithm);
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
int armv4_5_init_arch_info(struct target *target, struct arm *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;
|
|
}
|