X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Ftarget%2Fcortex_m3.c;h=48f811489f870929b396f31c0613d60d42268b7a;hb=d91941d5a01ca0b9d43571edc03ba18741076cca;hp=47052e9084a68d9a097d1842b8548551cf7ec4fe;hpb=833e7f5248778bcb31b4db1a1b91160995415203;p=openocd diff --git a/src/target/cortex_m3.c b/src/target/cortex_m3.c index 47052e90..48f81148 100644 --- a/src/target/cortex_m3.c +++ b/src/target/cortex_m3.c @@ -37,10 +37,18 @@ #include "target_type.h" #include "arm_disassembler.h" #include "register.h" +#include "arm_opcodes.h" /* NOTE: most of this should work fine for the Cortex-M1 and * Cortex-M0 cores too, although they're ARMv6-M not ARMv7-M. + * Some differences: M0/M1 doesn't have FBP remapping or the + * DWT tracing/profiling support. (So the cycle counter will + * not be usable; the other stuff isn't currently used here.) + * + * Although there are some workarounds for errata seen only in r0p0 + * silicon, such old parts are hard to find and thus not much tested + * any longer. */ @@ -51,11 +59,6 @@ static void cortex_m3_enable_watchpoints(struct target *target); static int cortex_m3_store_core_reg_u32(struct target *target, enum armv7m_regtype type, uint32_t num, uint32_t value); -#ifdef ARMV7_GDB_HACKS -extern uint8_t armv7m_gdb_dummy_cpsr_value[]; -extern struct reg armv7m_gdb_dummy_cpsr_reg; -#endif - static int cortexm3_dap_read_coreregister_u32(struct swjdp_common *swjdp, uint32_t *value, int regnum) { @@ -142,6 +145,7 @@ static int cortex_m3_clear_halt(struct target *target) /* Read Debug Fault Status Register */ mem_ap_read_atomic_u32(swjdp, NVIC_DFSR, &cortex_m3->nvic_dfsr); + /* Clear Debug Fault Status */ mem_ap_write_atomic_u32(swjdp, NVIC_DFSR, cortex_m3->nvic_dfsr); LOG_DEBUG(" NVIC_DFSR 0x%" PRIx32 "", cortex_m3->nvic_dfsr); @@ -158,10 +162,15 @@ static int cortex_m3_single_step_core(struct target *target) /* backup dhcsr reg */ dhcsr_save = cortex_m3->dcb_dhcsr; - /* mask interrupts if not done already */ + /* Mask interrupts before clearing halt, if done already. This avoids + * Erratum 377497 (fixed in r1p0) where setting MASKINTS while clearing + * HALT can put the core into an unknown state. + */ if (!(cortex_m3->dcb_dhcsr & C_MASKINTS)) - mem_ap_write_atomic_u32(swjdp, DCB_DHCSR, DBGKEY | C_MASKINTS | C_HALT | C_DEBUGEN); - mem_ap_write_atomic_u32(swjdp, DCB_DHCSR, DBGKEY | C_MASKINTS | C_STEP | C_DEBUGEN); + mem_ap_write_atomic_u32(swjdp, DCB_DHCSR, + DBGKEY | C_MASKINTS | C_HALT | C_DEBUGEN); + mem_ap_write_atomic_u32(swjdp, DCB_DHCSR, + DBGKEY | C_MASKINTS | C_STEP | C_DEBUGEN); LOG_DEBUG(" "); /* restore dhcsr reg */ @@ -176,14 +185,16 @@ static int cortex_m3_endreset_event(struct target *target) int i; uint32_t dcb_demcr; struct cortex_m3_common *cortex_m3 = target_to_cm3(target); + struct armv7m_common *armv7m = &cortex_m3->armv7m; struct swjdp_common *swjdp = &cortex_m3->armv7m.swjdp_info; struct cortex_m3_fp_comparator *fp_list = cortex_m3->fp_comparator_list; struct cortex_m3_dwt_comparator *dwt_list = cortex_m3->dwt_comparator_list; + /* REVISIT The four debug monitor bits are currently ignored... */ mem_ap_read_atomic_u32(swjdp, DCB_DEMCR, &dcb_demcr); LOG_DEBUG("DCB_DEMCR = 0x%8.8" PRIx32 "",dcb_demcr); - /* this regsiter is used for emulated dcc channel */ + /* this register is used for emulated dcc channel */ mem_ap_write_u32(swjdp, DCB_DCRDR, 0); /* Enable debug requests */ @@ -194,10 +205,18 @@ static int cortex_m3_endreset_event(struct target *target) /* clear any interrupt masking */ cortex_m3_write_debug_halt_mask(target, 0, C_MASKINTS); - /* Enable trace and dwt */ - mem_ap_write_u32(swjdp, DCB_DEMCR, TRCENA | VC_HARDERR | VC_BUSERR); - /* Monitor bus faults */ - mem_ap_write_u32(swjdp, NVIC_SHCSR, SHCSR_BUSFAULTENA); + /* Enable features controlled by ITM and DWT blocks, and catch only + * the vectors we were told to pay attention to. + * + * Target firmware is responsible for all fault handling policy + * choices *EXCEPT* explicitly scripted overrides like "vector_catch" + * or manual updates to the NVIC SHCSR and CCR registers. + */ + mem_ap_write_u32(swjdp, DCB_DEMCR, TRCENA | armv7m->demcr); + + /* Paranoia: evidently some (early?) chips don't preserve all the + * debug state (including FBP, DWT, etc) across reset... + */ /* Enable FPB */ target_write_u32(target, FP_CTRL, 3); @@ -312,6 +331,7 @@ static int cortex_m3_debug_entry(struct target *target) struct cortex_m3_common *cortex_m3 = target_to_cm3(target); struct armv7m_common *armv7m = &cortex_m3->armv7m; struct swjdp_common *swjdp = &armv7m->swjdp_info; + struct reg *r; LOG_DEBUG(" "); @@ -331,7 +351,8 @@ static int cortex_m3_debug_entry(struct target *target) armv7m->read_core_reg(target, i); } - xPSR = buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_xPSR].value, 0, 32); + r = armv7m->core_cache->reg_list + ARMV7M_xPSR; + xPSR = buf_get_u32(r->value, 0, 32); #ifdef ARMV7_GDB_HACKS /* FIXME this breaks on scan chains with more than one Cortex-M3. @@ -340,14 +361,14 @@ static int cortex_m3_debug_entry(struct target *target) /* copy real xpsr reg for gdb, setting thumb bit */ buf_set_u32(armv7m_gdb_dummy_cpsr_value, 0, 32, xPSR); buf_set_u32(armv7m_gdb_dummy_cpsr_value, 5, 1, 1); - armv7m_gdb_dummy_cpsr_reg.valid = armv7m->core_cache->reg_list[ARMV7M_xPSR].valid; - armv7m_gdb_dummy_cpsr_reg.dirty = armv7m->core_cache->reg_list[ARMV7M_xPSR].dirty; + armv7m_gdb_dummy_cpsr_reg.valid = r->valid; + armv7m_gdb_dummy_cpsr_reg.dirty = r->dirty; #endif /* For IT instructions xPSR must be reloaded on resume and clear on debug exec */ if (xPSR & 0xf00) { - armv7m->core_cache->reg_list[ARMV7M_xPSR].dirty = armv7m->core_cache->reg_list[ARMV7M_xPSR].valid; + r->dirty = r->valid; cortex_m3_store_core_reg_u32(target, ARMV7M_REGISTER_CORE_GP, 16, xPSR &~ 0xff); } @@ -359,7 +380,8 @@ static int cortex_m3_debug_entry(struct target *target) } else { - armv7m->core_mode = buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 1); + armv7m->core_mode = buf_get_u32(armv7m->core_cache + ->reg_list[ARMV7M_CONTROL].value, 0, 1); armv7m->exception_number = 0; } @@ -408,8 +430,11 @@ static int cortex_m3_poll(struct target *target) if (target->state == TARGET_RESET) { - /* Cannot switch context while running so endreset is called with target->state == TARGET_RESET */ - LOG_DEBUG("Exit from reset with dcb_dhcsr 0x%" PRIx32 "", cortex_m3->dcb_dhcsr); + /* Cannot switch context while running so endreset is + * called with target->state == TARGET_RESET + */ + LOG_DEBUG("Exit from reset with dcb_dhcsr 0x%" PRIx32, + cortex_m3->dcb_dhcsr); cortex_m3_endreset_event(target); target->state = TARGET_RUNNING; prev_target_state = TARGET_RUNNING; @@ -502,11 +527,13 @@ static int cortex_m3_soft_reset_halt(struct target *target) uint32_t dcb_dhcsr = 0; int retval, timeout = 0; - /* Enter debug state on reset, cf. end_reset_event() */ - mem_ap_write_u32(swjdp, DCB_DEMCR, TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET); + /* Enter debug state on reset; restore DEMCR in endreset_event() */ + mem_ap_write_u32(swjdp, DCB_DEMCR, + TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET); - /* Request a reset */ - mem_ap_write_atomic_u32(swjdp, NVIC_AIRCR, AIRCR_VECTKEY | AIRCR_VECTRESET); + /* Request a core-only reset */ + mem_ap_write_atomic_u32(swjdp, NVIC_AIRCR, + AIRCR_VECTKEY | AIRCR_VECTRESET); target->state = TARGET_RESET; /* registers are now invalid */ @@ -517,15 +544,23 @@ static int cortex_m3_soft_reset_halt(struct target *target) retval = mem_ap_read_atomic_u32(swjdp, DCB_DHCSR, &dcb_dhcsr); if (retval == ERROR_OK) { - mem_ap_read_atomic_u32(swjdp, NVIC_DFSR, &cortex_m3->nvic_dfsr); - if ((dcb_dhcsr & S_HALT) && (cortex_m3->nvic_dfsr & DFSR_VCATCH)) + mem_ap_read_atomic_u32(swjdp, NVIC_DFSR, + &cortex_m3->nvic_dfsr); + if ((dcb_dhcsr & S_HALT) + && (cortex_m3->nvic_dfsr & DFSR_VCATCH)) { - LOG_DEBUG("system reset-halted, dcb_dhcsr 0x%" PRIx32 ", nvic_dfsr 0x%" PRIx32 "", dcb_dhcsr, cortex_m3->nvic_dfsr); + LOG_DEBUG("system reset-halted, DHCSR 0x%08x, " + "DFSR 0x%08x", + (unsigned) dcb_dhcsr, + (unsigned) cortex_m3->nvic_dfsr); cortex_m3_poll(target); + /* FIXME restore user's vector catch config */ return ERROR_OK; } else - LOG_DEBUG("waiting for system reset-halt, dcb_dhcsr 0x%" PRIx32 ", %i ms", dcb_dhcsr, timeout); + LOG_DEBUG("waiting for system reset-halt, " + "DHCSR 0x%08x, %d ms", + (unsigned) dcb_dhcsr, timeout); } timeout++; alive_sleep(1); @@ -541,7 +576,7 @@ static void cortex_m3_enable_breakpoints(struct target *target) /* set any pending breakpoints */ while (breakpoint) { - if (breakpoint->set == 0) + if (!breakpoint->set) cortex_m3_set_breakpoint(target, breakpoint); breakpoint = breakpoint->next; } @@ -553,6 +588,7 @@ static int cortex_m3_resume(struct target *target, int current, struct armv7m_common *armv7m = target_to_armv7m(target); struct breakpoint *breakpoint = NULL; uint32_t resume_pc; + struct reg *r; if (target->state != TARGET_HALTED) { @@ -569,30 +605,40 @@ static int cortex_m3_resume(struct target *target, int current, if (debug_execution) { + r = armv7m->core_cache->reg_list + ARMV7M_PRIMASK; + /* Disable interrupts */ - /* We disable interrupts in the PRIMASK register instead of masking with C_MASKINTS, - * This is probably the same issue as Cortex-M3 Errata 377493: - * C_MASKINTS in parallel with disabled interrupts can cause local faults to not be taken. */ - buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_PRIMASK].value, 0, 32, 1); - armv7m->core_cache->reg_list[ARMV7M_PRIMASK].dirty = 1; - armv7m->core_cache->reg_list[ARMV7M_PRIMASK].valid = 1; + /* We disable interrupts in the PRIMASK register instead of + * masking with C_MASKINTS. This is probably the same issue + * as Cortex-M3 Erratum 377493 (fixed in r1p0): C_MASKINTS + * in parallel with disabled interrupts can cause local faults + * to not be taken. + * + * REVISIT this clearly breaks non-debug execution, since the + * PRIMASK register state isn't saved/restored... workaround + * by never resuming app code after debug execution. + */ + buf_set_u32(r->value, 0, 1, 1); + r->dirty = true; + r->valid = true; /* Make sure we are in Thumb mode */ - buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_xPSR].value, 0, 32, - buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_xPSR].value, 0, 32) | (1 << 24)); - armv7m->core_cache->reg_list[ARMV7M_xPSR].dirty = 1; - armv7m->core_cache->reg_list[ARMV7M_xPSR].valid = 1; + r = armv7m->core_cache->reg_list + ARMV7M_xPSR; + buf_set_u32(r->value, 24, 1, 1); + r->dirty = true; + r->valid = true; } /* current = 1: continue on current pc, otherwise continue at
*/ + r = armv7m->core_cache->reg_list + 15; if (!current) { - buf_set_u32(armv7m->core_cache->reg_list[15].value, 0, 32, address); - armv7m->core_cache->reg_list[15].dirty = 1; - armv7m->core_cache->reg_list[15].valid = 1; + buf_set_u32(r->value, 0, 32, address); + r->dirty = true; + r->valid = true; } - resume_pc = buf_get_u32(armv7m->core_cache->reg_list[15].value, 0, 32); + resume_pc = buf_get_u32(r->value, 0, 32); armv7m_restore_context(target); @@ -643,6 +689,7 @@ static int cortex_m3_step(struct target *target, int current, struct armv7m_common *armv7m = &cortex_m3->armv7m; struct swjdp_common *swjdp = &armv7m->swjdp_info; struct breakpoint *breakpoint = NULL; + struct reg *pc = armv7m->core_cache->reg_list + 15; if (target->state != TARGET_HALTED) { @@ -652,13 +699,12 @@ static int cortex_m3_step(struct target *target, int current, /* current = 1: continue on current pc, otherwise continue at */ if (!current) - buf_set_u32(cortex_m3->armv7m.core_cache->reg_list[15].value, - 0, 32, address); + buf_set_u32(pc->value, 0, 32, address); /* the front-end may request us not to handle breakpoints */ if (handle_breakpoints) { - breakpoint = breakpoint_find(target, buf_get_u32(armv7m - ->core_cache->reg_list[15].value, 0, 32)); + breakpoint = breakpoint_find(target, + buf_get_u32(pc->value, 0, 32)); if (breakpoint) cortex_m3_unset_breakpoint(target, breakpoint); } @@ -679,12 +725,16 @@ static int cortex_m3_step(struct target *target, int current, if (breakpoint) cortex_m3_set_breakpoint(target, breakpoint); - LOG_DEBUG("target stepped dcb_dhcsr = 0x%" PRIx32 " nvic_icsr = 0x%" PRIx32 "", cortex_m3->dcb_dhcsr, cortex_m3->nvic_icsr); + LOG_DEBUG("target stepped dcb_dhcsr = 0x%" PRIx32 + " nvic_icsr = 0x%" PRIx32, + cortex_m3->dcb_dhcsr, cortex_m3->nvic_icsr); cortex_m3_debug_entry(target); target_call_event_callbacks(target, TARGET_EVENT_HALTED); - LOG_DEBUG("target stepped dcb_dhcsr = 0x%" PRIx32 " nvic_icsr = 0x%" PRIx32 "", cortex_m3->dcb_dhcsr, cortex_m3->nvic_icsr); + LOG_DEBUG("target stepped dcb_dhcsr = 0x%" PRIx32 + " nvic_icsr = 0x%" PRIx32, + cortex_m3->dcb_dhcsr, cortex_m3->nvic_icsr); return ERROR_OK; } @@ -718,21 +768,25 @@ static int cortex_m3_assert_reset(struct target *target) { /* Set/Clear C_MASKINTS in a separate operation */ if (cortex_m3->dcb_dhcsr & C_MASKINTS) - mem_ap_write_atomic_u32(swjdp, DCB_DHCSR, DBGKEY | C_DEBUGEN | C_HALT); + mem_ap_write_atomic_u32(swjdp, DCB_DHCSR, + DBGKEY | C_DEBUGEN | C_HALT); /* clear any debug flags before resuming */ cortex_m3_clear_halt(target); /* clear C_HALT in dhcsr reg */ cortex_m3_write_debug_halt_mask(target, 0, C_HALT); - - /* Enter debug state on reset, cf. end_reset_event() */ - mem_ap_write_u32(swjdp, DCB_DEMCR, TRCENA | VC_HARDERR | VC_BUSERR); } else { - /* Enter debug state on reset, cf. end_reset_event() */ - mem_ap_write_atomic_u32(swjdp, DCB_DEMCR, TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET); + /* Halt in debug on reset; endreset_event() restores DEMCR. + * + * REVISIT catching BUSERR presumably helps to defend against + * bad vector table entries. Should this include MMERR or + * other flags too? + */ + mem_ap_write_atomic_u32(swjdp, DCB_DEMCR, + TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET); } /* @@ -880,16 +934,25 @@ cortex_m3_set_breakpoint(struct target *target, struct breakpoint *breakpoint) else if (breakpoint->type == BKPT_SOFT) { uint8_t code[4]; - buf_set_u32(code, 0, 32, ARMV7M_T_BKPT(0x11)); - if ((retval = target_read_memory(target, breakpoint->address & 0xFFFFFFFE, breakpoint->length, 1, breakpoint->orig_instr)) != ERROR_OK) - { + + /* NOTE: on ARMv6-M and ARMv7-M, BKPT(0xab) is used for + * semihosting; don't use that. Otherwise the BKPT + * parameter is arbitrary. + */ + buf_set_u32(code, 0, 32, ARMV5_T_BKPT(0x11)); + retval = target_read_memory(target, + breakpoint->address & 0xFFFFFFFE, + breakpoint->length, 1, + breakpoint->orig_instr); + if (retval != ERROR_OK) return retval; - } - if ((retval = target_write_memory(target, breakpoint->address & 0xFFFFFFFE, breakpoint->length, 1, code)) != ERROR_OK) - { + retval = target_write_memory(target, + breakpoint->address & 0xFFFFFFFE, + breakpoint->length, 1, + code); + if (retval != ERROR_OK) return retval; - } - breakpoint->set = 0x11; /* Any nice value but 0 */ + breakpoint->set = true; } LOG_DEBUG("BPID: %d, Type: %d, Address: 0x%08" PRIx32 " Length: %d (set=%d)", @@ -952,7 +1015,7 @@ cortex_m3_unset_breakpoint(struct target *target, struct breakpoint *breakpoint) } } } - breakpoint->set = 0; + breakpoint->set = false; return ERROR_OK; } @@ -1131,7 +1194,7 @@ cortex_m3_unset_watchpoint(struct target *target, struct watchpoint *watchpoint) target_write_u32(target, comparator->dwt_comparator_address + 8, comparator->function); - watchpoint->set = 0; + watchpoint->set = false; return ERROR_OK; } @@ -1141,13 +1204,6 @@ cortex_m3_add_watchpoint(struct target *target, struct watchpoint *watchpoint) { struct cortex_m3_common *cortex_m3 = target_to_cm3(target); - /* REVISIT why check? DWT can be updated with core running ... */ - if (target->state != TARGET_HALTED) - { - LOG_WARNING("target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - if (cortex_m3->dwt_comp_available < 1) { LOG_DEBUG("no comparators?"); @@ -1224,7 +1280,7 @@ static void cortex_m3_enable_watchpoints(struct target *target) /* set any pending watchpoints */ while (watchpoint) { - if (watchpoint->set == 0) + if (!watchpoint->set) cortex_m3_set_watchpoint(target, watchpoint); watchpoint = watchpoint->next; } @@ -1322,8 +1378,11 @@ static int cortex_m3_store_core_reg_u32(struct target *target, retval = cortexm3_dap_write_coreregister_u32(swjdp, value, num); if (retval != ERROR_OK) { + struct reg *r; + LOG_ERROR("JTAG failure %i", retval); - armv7m->core_cache->reg_list[num].dirty = armv7m->core_cache->reg_list[num].valid; + r = armv7m->core_cache->reg_list + num; + r->dirty = r->valid; return ERROR_JTAG_DEVICE_ERROR; } LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", (int)num, value); @@ -1466,6 +1525,9 @@ struct dwt_reg { static struct dwt_reg dwt_base_regs[] = { { DWT_CTRL, "dwt_ctrl", 32, }, + /* NOTE that Erratum 532314 (fixed r2p0) affects CYCCNT: it wrongly + * increments while the core is asleep. + */ { DWT_CYCCNT, "dwt_cyccnt", 32, }, /* plus some 8 bit counters, useful for profiling with TPIU */ }; @@ -1592,7 +1654,8 @@ static int cortex_m3_examine(struct target *target) return retval; if (((cpuid >> 4) & 0xc3f) == 0xc23) - LOG_DEBUG("CORTEX-M3 processor detected"); + LOG_DEBUG("Cortex-M3 r%dp%d processor detected", + (cpuid >> 20) & 0xf, (cpuid >> 0) & 0xf); LOG_DEBUG("cpuid: 0x%8.8" PRIx32 "", cpuid); /* NOTE: FPB and DWT are both optional. */ @@ -1614,6 +1677,12 @@ static int cortex_m3_examine(struct target *target) /* Setup DWT */ cortex_m3_dwt_setup(cortex_m3, target); + + /* These hardware breakpoints only work for code in flash! */ + LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints", + target_name(target), + cortex_m3->fp_num_code, + cortex_m3->dwt_num_comp); } return ERROR_OK; @@ -1864,12 +1933,20 @@ COMMAND_HANDLER(handle_cortex_m3_vector_catch_command) } } write: + /* For now, armv7m->demcr only stores vector catch flags. */ + armv7m->demcr = catch; + demcr &= ~0xffff; demcr |= catch; - /* write, but don't assume it stuck */ + /* write, but don't assume it stuck (why not??) */ mem_ap_write_u32(swjdp, DCB_DEMCR, demcr); mem_ap_read_atomic_u32(swjdp, DCB_DEMCR, &demcr); + + /* FIXME be sure to clear DEMCR on clean server shutdown. + * Otherwise the vector catch hardware could fire when there's + * no debugger hooked up, causing much confusion... + */ } for (unsigned i = 0; i < ARRAY_SIZE(vec_ids); i++) @@ -1912,28 +1989,42 @@ COMMAND_HANDLER(handle_cortex_m3_mask_interrupts_command) return ERROR_OK; } -static int cortex_m3_register_commands(struct command_context *cmd_ctx) -{ - int retval; - struct command *cortex_m3_cmd; - - retval = armv7m_register_commands(cmd_ctx); - - cortex_m3_cmd = COMMAND_REGISTER(cmd_ctx, NULL, "cortex_m3", - NULL, COMMAND_ANY, "cortex_m3 specific commands"); - - COMMAND_REGISTER(cmd_ctx, cortex_m3_cmd, "disassemble", - handle_cortex_m3_disassemble_command, COMMAND_EXEC, - "disassemble Thumb2 instructions [