X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Ftarget%2Farmv4_5.c;h=80c06ef31d355a7c4f5f523b928935a4066ea06c;hb=6eee0729d79eab496d1d4368a2bae7e4e2d19876;hp=01d3bc3076cac743b262d43c5953143530094228;hpb=5706fd7860ea01c591ecf74880a5a5e04e6df22e;p=openocd.git diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 01d3bc3076..80c06ef31d 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -31,7 +31,7 @@ #include "arm_jtag.h" #include "breakpoints.h" #include "arm_disassembler.h" -#include "binarybuffer.h" +#include #include "algorithm.h" #include "register.h" @@ -245,6 +245,10 @@ static const struct { unsigned cookie; enum armv4_5_mode mode; } arm_core_regs[] = { + /* IMPORTANT: we guarantee that the first eight cached registers + * correspond to r0..r7, and the fifteenth to PC, so that callers + * don't need to map them. + */ { .name = "r0", .cookie = 0, .mode = ARMV4_5_MODE_ANY, }, { .name = "r1", .cookie = 1, .mode = ARMV4_5_MODE_ANY, }, { .name = "r2", .cookie = 2, .mode = ARMV4_5_MODE_ANY, }, @@ -255,7 +259,8 @@ static const struct { { .name = "r7", .cookie = 7, .mode = ARMV4_5_MODE_ANY, }, /* NOTE: regs 8..12 might be shadowed by FIQ ... flagging - * them as MODE_ANY creates special cases. + * them as MODE_ANY creates special cases. (ANY means + * "not mapped" elsewhere; here it's "everything but FIQ".) */ { .name = "r8", .cookie = 8, .mode = ARMV4_5_MODE_ANY, }, { .name = "r9", .cookie = 9, .mode = ARMV4_5_MODE_ANY, }, @@ -267,6 +272,7 @@ static const struct { { .name = "sp_usr", .cookie = 13, .mode = ARMV4_5_MODE_USR, }, { .name = "lr_usr", .cookie = 14, .mode = ARMV4_5_MODE_USR, }, + /* guaranteed to be at index 15 */ { .name = "pc", .cookie = 15, .mode = ARMV4_5_MODE_ANY, }, { .name = "r8_fiq", .cookie = 8, .mode = ARMV4_5_MODE_FIQ, }, @@ -275,20 +281,20 @@ static const struct { { .name = "r11_fiq", .cookie = 11, .mode = ARMV4_5_MODE_FIQ, }, { .name = "r12_fiq", .cookie = 12, .mode = ARMV4_5_MODE_FIQ, }, - { .name = "lr_fiq", .cookie = 13, .mode = ARMV4_5_MODE_FIQ, }, - { .name = "sp_fiq", .cookie = 14, .mode = ARMV4_5_MODE_FIQ, }, + { .name = "sp_fiq", .cookie = 13, .mode = ARMV4_5_MODE_FIQ, }, + { .name = "lr_fiq", .cookie = 14, .mode = ARMV4_5_MODE_FIQ, }, - { .name = "lr_irq", .cookie = 13, .mode = ARMV4_5_MODE_IRQ, }, - { .name = "sp_irq", .cookie = 14, .mode = ARMV4_5_MODE_IRQ, }, + { .name = "sp_irq", .cookie = 13, .mode = ARMV4_5_MODE_IRQ, }, + { .name = "lr_irq", .cookie = 14, .mode = ARMV4_5_MODE_IRQ, }, - { .name = "lr_svc", .cookie = 13, .mode = ARMV4_5_MODE_SVC, }, - { .name = "sp_svc", .cookie = 14, .mode = ARMV4_5_MODE_SVC, }, + { .name = "sp_svc", .cookie = 13, .mode = ARMV4_5_MODE_SVC, }, + { .name = "lr_svc", .cookie = 14, .mode = ARMV4_5_MODE_SVC, }, - { .name = "lr_abt", .cookie = 13, .mode = ARMV4_5_MODE_ABT, }, - { .name = "sp_abt", .cookie = 14, .mode = ARMV4_5_MODE_ABT, }, + { .name = "sp_abt", .cookie = 13, .mode = ARMV4_5_MODE_ABT, }, + { .name = "lr_abt", .cookie = 14, .mode = ARMV4_5_MODE_ABT, }, - { .name = "lr_und", .cookie = 13, .mode = ARMV4_5_MODE_UND, }, - { .name = "sp_und", .cookie = 14, .mode = ARMV4_5_MODE_UND, }, + { .name = "sp_und", .cookie = 13, .mode = ARMV4_5_MODE_UND, }, + { .name = "lr_und", .cookie = 14, .mode = ARMV4_5_MODE_UND, }, { .name = "cpsr", .cookie = 16, .mode = ARMV4_5_MODE_ANY, }, { .name = "spsr_fiq", .cookie = 16, .mode = ARMV4_5_MODE_FIQ, }, @@ -297,8 +303,8 @@ static const struct { { .name = "spsr_abt", .cookie = 16, .mode = ARMV4_5_MODE_ABT, }, { .name = "spsr_und", .cookie = 16, .mode = ARMV4_5_MODE_UND, }, - { .name = "lr_mon", .cookie = 13, .mode = ARM_MODE_MON, }, - { .name = "sp_mon", .cookie = 14, .mode = ARM_MODE_MON, }, + { .name = "sp_mon", .cookie = 13, .mode = ARM_MODE_MON, }, + { .name = "lr_mon", .cookie = 14, .mode = ARM_MODE_MON, }, { .name = "spsr_mon", .cookie = 16, .mode = ARM_MODE_MON, }, }; @@ -333,6 +339,95 @@ const int armv4_5_core_reg_map[8][17] = } }; +/** + * Configures host-side ARM records to reflect the specified CPSR. + * Later, code can use arm_reg_current() to map register numbers + * according to how they are exposed by this mode. + */ +void arm_set_cpsr(struct arm *arm, uint32_t cpsr) +{ + enum armv4_5_mode mode = cpsr & 0x1f; + int num; + + /* NOTE: this may be called very early, before the register + * cache is set up. We can't defend against many errors, in + * particular against CPSRs that aren't valid *here* ... + */ + if (arm->cpsr) { + buf_set_u32(arm->cpsr->value, 0, 32, cpsr); + arm->cpsr->valid = 1; + arm->cpsr->dirty = 0; + } + + arm->core_mode = mode; + + /* mode_to_number() warned; set up a somewhat-sane mapping */ + num = armv4_5_mode_to_number(mode); + if (num < 0) { + mode = ARMV4_5_MODE_USR; + num = 0; + } + + arm->map = &armv4_5_core_reg_map[num][0]; + arm->spsr = (mode == ARMV4_5_MODE_USR || mode == ARMV4_5_MODE_SYS) + ? NULL + : arm->core_cache->reg_list + arm->map[16]; + + /* Older ARMs won't have the J bit */ + enum armv4_5_state state; + + if (cpsr & (1 << 5)) { /* T */ + if (cpsr & (1 << 24)) { /* J */ + LOG_WARNING("ThumbEE -- incomplete support"); + state = ARM_STATE_THUMB_EE; + } else + state = ARMV4_5_STATE_THUMB; + } else { + if (cpsr & (1 << 24)) { /* J */ + LOG_ERROR("Jazelle state handling is BROKEN!"); + state = ARMV4_5_STATE_JAZELLE; + } else + state = ARMV4_5_STATE_ARM; + } + arm->core_state = state; + + LOG_DEBUG("set CPSR %#8.8x: %s mode, %s state", (unsigned) cpsr, + arm_mode_name(mode), + armv4_5_state_strings[arm->core_state]); +} + +/** + * Returns handle to the register currently mapped to a given number. + * Someone must have called arm_set_cpsr() before. + * + * \param arm This core's state and registers are used. + * \param regnum From 0..15 corresponding to R0..R14 and PC. + * Note that R0..R7 don't require mapping; you may access those + * as the first eight entries in the register cache. Likewise + * R15 (PC) doesn't need mapping; you may also access it directly. + * However, R8..R14, and SPSR (arm->spsr) *must* be mapped. + * CPSR (arm->cpsr) is also not mapped. + */ +struct reg *arm_reg_current(struct arm *arm, unsigned regnum) +{ + struct reg *r; + + if (regnum > 16) + return NULL; + + r = arm->core_cache->reg_list + arm->map[regnum]; + + /* e.g. invalid CPSR said "secure monitor" mode on a core + * that doesn't support it... + */ + if (!r) { + LOG_ERROR("Invalid CPSR mode"); + r = arm->core_cache->reg_list + regnum; + } + + return r; +} + static const uint8_t arm_gdb_dummy_fp_value[12]; /** @@ -396,7 +491,7 @@ static int armv4_5_set_core_reg(struct reg *reg, uint8_t *buf) { struct arm_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); + struct arm *armv4_5_target = target_to_armv4_5(target); uint32_t value = buf_get_u32(buf, 0, 32); if (target->state != TARGET_HALTED) @@ -408,50 +503,27 @@ static int armv4_5_set_core_reg(struct reg *reg, uint8_t *buf) /* Except for CPSR, the "reg" command exposes a writeback model * for the register cache. */ - buf_set_u32(reg->value, 0, 32, value); - reg->dirty = 1; - reg->valid = 1; - - if (reg == armv4_5_target->cpsr) - { - /* FIXME handle J bit too; mostly for ThumbEE, also Jazelle */ - 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 (reg == armv4_5_target->cpsr) { + arm_set_cpsr(armv4_5_target, value); - /* REVISIT Why only update core for mode change, not also - * for state changes? Possibly older cores need to stay - * in ARM mode during halt mode debug, not execute Thumb; - * v6/v7a/v7r seem to do that automatically... + /* Older cores need help to be in ARM mode during halt + * mode debug, so we clear the J and T bits if we flush. + * For newer cores (v6/v7a/v7r) we don't need that, but + * it won't hurt since CPSR is always flushed anyway. */ - - if (armv4_5_target->core_mode != (enum armv4_5_mode)(value & 0x1f)) - { + if (armv4_5_target->core_mode != + (enum armv4_5_mode)(value & 0x1f)) { LOG_DEBUG("changing ARM core mode to '%s'", arm_mode_name(value & 0x1f)); - armv4_5_target->core_mode = value & 0x1f; + value &= ~((1 << 24) | (1 << 5)); armv4_5_target->write_core_reg(target, reg, 16, ARMV4_5_MODE_ANY, value); - reg->dirty = 0; } + } else { + buf_set_u32(reg->value, 0, 32, value); + reg->valid = 1; } + reg->dirty = 1; return ERROR_OK; } @@ -511,7 +583,7 @@ struct reg_cache* armv4_5_build_reg_cache(struct target *target, struct arm *arm int armv4_5_arch_state(struct target *target) { - struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target); + struct arm *armv4_5 = target_to_armv4_5(target); if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) { @@ -535,7 +607,7 @@ int armv4_5_arch_state(struct target *target) COMMAND_HANDLER(handle_armv4_5_reg_command) { struct target *target = get_current_target(CMD_CTX); - struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target); + struct arm *armv4_5 = target_to_armv4_5(target); unsigned num_regs; struct reg *regs; @@ -622,7 +694,7 @@ COMMAND_HANDLER(handle_armv4_5_reg_command) 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); + struct arm *armv4_5 = target_to_armv4_5(target); if (!is_arm(armv4_5)) { @@ -718,31 +790,187 @@ usage: return retval; } -int armv4_5_register_commands(struct command_context *cmd_ctx) +static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - struct command *armv4_5_cmd; - - armv4_5_cmd = register_command(cmd_ctx, NULL, "arm", - NULL, COMMAND_ANY, - "generic ARM 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 "); - register_command(cmd_ctx, armv4_5_cmd, "disassemble", - handle_armv4_5_disassemble_command, COMMAND_EXEC, - "disassemble instructions " - "
[ ['thumb']]"); + struct command_context *context; + struct target *target; + struct arm *arm; + int retval; - return ERROR_OK; + context = Jim_GetAssocData(interp, "context"); + if (context == NULL) { + LOG_ERROR("%s: no command context", __func__); + return JIM_ERR; + } + target = get_current_target(context); + if (target == NULL) { + LOG_ERROR("%s: no current target", __func__); + return JIM_ERR; + } + if (!target_was_examined(target)) { + LOG_ERROR("%s: not yet examined", target_name(target)); + return JIM_ERR; + } + arm = target_to_arm(target); + if (!is_arm(arm)) { + LOG_ERROR("%s: not an ARM", target_name(target)); + return JIM_ERR; + } + + if ((argc < 6) || (argc > 7)) { + /* FIXME use the command name to verify # params... */ + LOG_ERROR("%s: wrong number of arguments", __func__); + return JIM_ERR; + } + + int cpnum; + uint32_t op1; + uint32_t op2; + uint32_t CRn; + uint32_t CRm; + uint32_t value; + long l; + + /* NOTE: parameter sequence matches ARM instruction set usage: + * MCR pNUM, op1, rX, CRn, CRm, op2 ; write CP from rX + * MRC pNUM, op1, rX, CRn, CRm, op2 ; read CP into rX + * The "rX" is necessarily omitted; it uses Tcl mechanisms. + */ + retval = Jim_GetLong(interp, argv[1], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0xf) { + LOG_ERROR("%s: %s %d out of range", __func__, + "coprocessor", (int) l); + return JIM_ERR; + } + cpnum = l; + + retval = Jim_GetLong(interp, argv[2], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0x7) { + LOG_ERROR("%s: %s %d out of range", __func__, + "op1", (int) l); + return JIM_ERR; + } + op1 = l; + + retval = Jim_GetLong(interp, argv[3], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0xf) { + LOG_ERROR("%s: %s %d out of range", __func__, + "CRn", (int) l); + return JIM_ERR; + } + CRn = l; + + retval = Jim_GetLong(interp, argv[4], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0xf) { + LOG_ERROR("%s: %s %d out of range", __func__, + "CRm", (int) l); + return JIM_ERR; + } + CRm = l; + + retval = Jim_GetLong(interp, argv[5], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0x7) { + LOG_ERROR("%s: %s %d out of range", __func__, + "op2", (int) l); + return JIM_ERR; + } + op2 = l; + + value = 0; + + /* FIXME don't assume "mrc" vs "mcr" from the number of params; + * that could easily be a typo! Check both... + * + * FIXME change the call syntax here ... simplest to just pass + * the MRC() or MCR() instruction to be executed. That will also + * let us support the "mrc2" and "mcr2" opcodes (toggling one bit) + * if that's ever needed. + */ + if (argc == 7) { + retval = Jim_GetLong(interp, argv[6], &l); + if (retval != JIM_OK) { + return retval; + } + value = l; + + /* NOTE: parameters reordered! */ + // ARMV4_5_MCR(cpnum, op1, 0, CRn, CRm, op2) + retval = arm->mcr(target, cpnum, op1, op2, CRn, CRm, value); + if (retval != ERROR_OK) + return JIM_ERR; + } else { + /* NOTE: parameters reordered! */ + // ARMV4_5_MRC(cpnum, op1, 0, CRn, CRm, op2) + retval = arm->mrc(target, cpnum, op1, op2, CRn, CRm, &value); + if (retval != ERROR_OK) + return JIM_ERR; + + Jim_SetResult(interp, Jim_NewIntObj(interp, value)); + } + + return JIM_OK; } +static const struct command_registration arm_exec_command_handlers[] = { + { + .name = "reg", + .handler = &handle_armv4_5_reg_command, + .mode = COMMAND_EXEC, + .help = "display ARM core registers", + }, + { + .name = "core_state", + .handler = &handle_armv4_5_core_state_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "display/change ARM core state", + }, + { + .name = "disassemble", + .handler = &handle_armv4_5_disassemble_command, + .mode = COMMAND_EXEC, + .usage = "
[ ['thumb']]", + .help = "disassemble instructions ", + }, + { + .name = "mcr", + .mode = COMMAND_EXEC, + .jim_handler = &jim_mcrmrc, + .help = "write coprocessor register", + .usage = "cpnum op1 CRn op2 CRm value", + }, + { + .name = "mrc", + .jim_handler = &jim_mcrmrc, + .help = "read coprocessor register", + .usage = "cpnum op1 CRn op2 CRm", + }, + + COMMAND_REGISTRATION_DONE +}; +const struct command_registration arm_command_handlers[] = { + { + .name = "arm", + .mode = COMMAND_ANY, + .help = "ARM command group", + .chain = arm_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + 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); + struct arm *armv4_5 = target_to_armv4_5(target); int i; if (!is_arm_mode(armv4_5->core_mode)) @@ -752,14 +980,10 @@ int armv4_5_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *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); - } + (*reg_list)[i] = arm_reg_current(armv4_5, i); for (i = 16; i < 24; i++) - { (*reg_list)[i] = &arm_gdb_dummy_fp_reg; - } (*reg_list)[24] = &arm_gdb_dummy_fps_reg; (*reg_list)[25] = armv4_5->cpsr; @@ -771,7 +995,7 @@ int armv4_5_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int 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); + struct arm *armv4_5 = target_to_armv4_5(target); if ((retval = target_wait_state(target, TARGET_HALTED, timeout_ms)) != ERROR_OK) { @@ -800,17 +1024,23 @@ static int armv4_5_run_algorithm_completion(struct target *target, uint32_t exit 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)) +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 arm *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) @@ -835,6 +1065,9 @@ int armv4_5_run_algorithm_inner(struct target *target, int num_mem_params, struc return ERROR_FAIL; } + /* save r0..pc, cpsr-or-spsr, and then cpsr-for-sure; + * they'll be restored later. + */ for (i = 0; i <= 16; i++) { struct reg *r; @@ -952,6 +1185,7 @@ int armv4_5_run_algorithm_inner(struct target *target, int num_mem_params, struc } } + /* restore everything we saved before (17 or 18 registers) */ for (i = 0; i <= 16; i++) { uint32_t regvalue; @@ -964,12 +1198,11 @@ int armv4_5_run_algorithm_inner(struct target *target, int num_mem_params, struc ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).dirty = 1; } } - buf_set_u32(armv4_5->cpsr->value, 0, 32, cpsr); - armv4_5->cpsr->valid = 1; + + arm_set_cpsr(armv4_5, cpsr); armv4_5->cpsr->dirty = 1; armv4_5->core_state = core_state; - armv4_5->core_mode = core_mode; return retval; } @@ -1151,7 +1384,7 @@ int arm_blank_check_memory(struct target *target, static int arm_full_context(struct target *target) { - struct armv4_5_common_s *armv4_5 = target_to_armv4_5(target); + struct arm *armv4_5 = target_to_armv4_5(target); unsigned num_regs = armv4_5->core_cache->num_regs; struct reg *reg = armv4_5->core_cache->reg_list; int retval = ERROR_OK; @@ -1164,13 +1397,31 @@ static int arm_full_context(struct target *target) return retval; } +static int arm_default_mrc(struct target *target, int cpnum, + uint32_t op1, uint32_t op2, + uint32_t CRn, uint32_t CRm, + uint32_t *value) +{ + LOG_ERROR("%s doesn't implement MRC", target_type_name(target)); + return ERROR_FAIL; +} + +static int arm_default_mcr(struct target *target, int cpnum, + uint32_t op1, uint32_t op2, + uint32_t CRn, uint32_t CRm, + uint32_t value) +{ + LOG_ERROR("%s doesn't implement MCR", target_type_name(target)); + return ERROR_FAIL; +} + int armv4_5_init_arch_info(struct target *target, struct arm *armv4_5) { target->arch_info = armv4_5; + armv4_5->target = target; armv4_5->common_magic = ARMV4_5_COMMON_MAGIC; - armv4_5->core_state = ARMV4_5_STATE_ARM; - armv4_5->core_mode = ARMV4_5_MODE_USR; + arm_set_cpsr(armv4_5, ARMV4_5_MODE_USR); /* core_type may be overridden by subtype logic */ armv4_5->core_type = ARMV4_5_MODE_ANY; @@ -1179,5 +1430,10 @@ int armv4_5_init_arch_info(struct target *target, struct arm *armv4_5) if (!armv4_5->full_context && armv4_5->read_core_reg) armv4_5->full_context = arm_full_context; + if (!armv4_5->mrc) + armv4_5->mrc = arm_default_mrc; + if (!armv4_5->mcr) + armv4_5->mcr = arm_default_mcr; + return ERROR_OK; }