X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Fcortex_m3.c;h=3ebc34ad7fadb6ea6b9dccbfce5a6034c29eb384;hp=bb0b0c416be85b5d582ee8ab37842d4346f9996a;hb=3b68a708c2b039d9b091608eccb2206725742a47;hpb=98723c4ecdbe06f90c66f3abec27b792c3b38e34 diff --git a/src/target/cortex_m3.c b/src/target/cortex_m3.c index bb0b0c416b..3ebc34ad7f 100644 --- a/src/target/cortex_m3.c +++ b/src/target/cortex_m3.c @@ -31,18 +31,26 @@ #include "config.h" #endif +#include "breakpoints.h" #include "cortex_m3.h" #include "target_request.h" #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. */ -#define ARRAY_SIZE(x) ((int)(sizeof(x)/sizeof((x)[0]))) - /* forward declarations */ static int cortex_m3_set_breakpoint(struct target *target, struct breakpoint *breakpoint); @@ -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) { @@ -67,8 +70,6 @@ static int cortexm3_dap_read_coreregister_u32(struct swjdp_common *swjdp, mem_ap_read_u32(swjdp, DCB_DCRDR, &dcrdr); - swjdp->trans_mode = TRANS_MODE_COMPOSITE; - /* mem_ap_write_u32(swjdp, DCB_DCRSR, regnum); */ dap_setup_accessport(swjdp, CSW_32BIT | CSW_ADDRINC_OFF, DCB_DCRSR & 0xFFFFFFF0); dap_ap_write_reg_u32(swjdp, AP_REG_BD0 | (DCB_DCRSR & 0xC), regnum); @@ -77,7 +78,7 @@ static int cortexm3_dap_read_coreregister_u32(struct swjdp_common *swjdp, dap_setup_accessport(swjdp, CSW_32BIT | CSW_ADDRINC_OFF, DCB_DCRDR & 0xFFFFFFF0); dap_ap_read_reg_u32(swjdp, AP_REG_BD0 | (DCB_DCRDR & 0xC), value); - retval = swjdp_transaction_endcheck(swjdp); + retval = jtagdp_transaction_endcheck(swjdp); /* restore DCB_DCRDR - this needs to be in a seperate * transaction otherwise the emulated DCC channel breaks */ @@ -98,8 +99,6 @@ static int cortexm3_dap_write_coreregister_u32(struct swjdp_common *swjdp, mem_ap_read_u32(swjdp, DCB_DCRDR, &dcrdr); - swjdp->trans_mode = TRANS_MODE_COMPOSITE; - /* mem_ap_write_u32(swjdp, DCB_DCRDR, core_regs[i]); */ dap_setup_accessport(swjdp, CSW_32BIT | CSW_ADDRINC_OFF, DCB_DCRDR & 0xFFFFFFF0); dap_ap_write_reg_u32(swjdp, AP_REG_BD0 | (DCB_DCRDR & 0xC), value); @@ -108,7 +107,7 @@ static int cortexm3_dap_write_coreregister_u32(struct swjdp_common *swjdp, dap_setup_accessport(swjdp, CSW_32BIT | CSW_ADDRINC_OFF, DCB_DCRSR & 0xFFFFFFF0); dap_ap_write_reg_u32(swjdp, AP_REG_BD0 | (DCB_DCRSR & 0xC), regnum | DCRSR_WnR); - retval = swjdp_transaction_endcheck(swjdp); + retval = jtagdp_transaction_endcheck(swjdp); /* restore DCB_DCRDR - this needs to be in a seperate * transaction otherwise the emulated DCC channel breaks */ @@ -142,6 +141,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 +158,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 +181,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 +201,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); @@ -219,9 +234,9 @@ static int cortex_m3_endreset_event(struct target *target) target_write_u32(target, dwt_list[i].dwt_comparator_address + 8, dwt_list[i].function); } - swjdp_transaction_endcheck(swjdp); + jtagdp_transaction_endcheck(swjdp); - armv7m_invalidate_core_regs(target); + register_cache_invalidate(cortex_m3->armv7m.core_cache); /* make sure we have latest dhcsr flags */ mem_ap_read_atomic_u32(swjdp, DCB_DHCSR, &cortex_m3->dcb_dhcsr); @@ -298,12 +313,30 @@ static int cortex_m3_examine_exception_reason(struct target *target) except_sr = 0; break; } - swjdp_transaction_endcheck(swjdp); + jtagdp_transaction_endcheck(swjdp); LOG_DEBUG("%s SHCSR 0x%" PRIx32 ", SR 0x%" PRIx32 ", CFSR 0x%" PRIx32 ", AR 0x%" PRIx32 "", armv7m_exception_string(armv7m->exception_number), \ shcsr, except_sr, cfsr, except_ar); return ERROR_OK; } +/* PSP is used in some thread modes */ +static const int armv7m_psp_reg_map[17] = { + ARMV7M_R0, ARMV7M_R1, ARMV7M_R2, ARMV7M_R3, + ARMV7M_R4, ARMV7M_R5, ARMV7M_R6, ARMV7M_R7, + ARMV7M_R8, ARMV7M_R9, ARMV7M_R10, ARMV7M_R11, + ARMV7M_R12, ARMV7M_PSP, ARMV7M_R14, ARMV7M_PC, + ARMV7M_xPSR, +}; + +/* MSP is used in handler and some thread modes */ +static const int armv7m_msp_reg_map[17] = { + ARMV7M_R0, ARMV7M_R1, ARMV7M_R2, ARMV7M_R3, + ARMV7M_R4, ARMV7M_R5, ARMV7M_R6, ARMV7M_R7, + ARMV7M_R8, ARMV7M_R9, ARMV7M_R10, ARMV7M_R11, + ARMV7M_R12, ARMV7M_MSP, ARMV7M_R14, ARMV7M_PC, + ARMV7M_xPSR, +}; + static int cortex_m3_debug_entry(struct target *target) { int i; @@ -311,7 +344,9 @@ static int cortex_m3_debug_entry(struct target *target) int retval; struct cortex_m3_common *cortex_m3 = target_to_cm3(target); struct armv7m_common *armv7m = &cortex_m3->armv7m; + struct arm *arm = &armv7m->arm; struct swjdp_common *swjdp = &armv7m->swjdp_info; + struct reg *r; LOG_DEBUG(" "); @@ -331,20 +366,24 @@ 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. + * Instead, each CM3 should have its own dummy value... + */ /* 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); } @@ -353,10 +392,27 @@ static int cortex_m3_debug_entry(struct target *target) { armv7m->core_mode = ARMV7M_MODE_HANDLER; armv7m->exception_number = (xPSR & 0x1FF); + + arm->core_mode = ARM_MODE_HANDLER; + arm->map = armv7m_msp_reg_map; } else { - armv7m->core_mode = buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 1); + unsigned control = buf_get_u32(armv7m->core_cache + ->reg_list[ARMV7M_CONTROL].value, 0, 2); + + /* is this thread privileged? */ + armv7m->core_mode = control & 1; + arm->core_mode = armv7m->core_mode + ? ARM_MODE_USER_THREAD + : ARM_MODE_THREAD; + + /* which stack is it using? */ + if (control & 2) + arm->map = armv7m_psp_reg_map; + else + arm->map = armv7m_msp_reg_map; + armv7m->exception_number = 0; } @@ -367,7 +423,7 @@ static int cortex_m3_debug_entry(struct target *target) LOG_DEBUG("entered debug state in core mode: %s at PC 0x%" PRIx32 ", target->state: %s", armv7m_mode_strings[armv7m->core_mode], - *(uint32_t*)(armv7m->core_cache->reg_list[15].value), + *(uint32_t*)(arm->pc->value), target_state_name(target)); if (armv7m->post_debug_entry) @@ -391,6 +447,21 @@ static int cortex_m3_poll(struct target *target) return retval; } + /* Recover from lockup. See ARMv7-M architecture spec, + * section B1.5.15 "Unrecoverable exception cases". + * + * REVISIT Is there a better way to report and handle this? + */ + if (cortex_m3->dcb_dhcsr & S_LOCKUP) { + LOG_WARNING("%s -- clearing lockup after double fault", + target_name(target)); + cortex_m3_write_debug_halt_mask(target, C_HALT, 0); + target->debug_reason = DBG_REASON_DBGRQ; + + /* refresh status bits */ + mem_ap_read_atomic_u32(swjdp, DCB_DHCSR, &cortex_m3->dcb_dhcsr); + } + if (cortex_m3->dcb_dhcsr & S_RESET_ST) { /* check if still in reset */ @@ -405,8 +476,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; @@ -499,30 +573,40 @@ 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 */ - armv7m_invalidate_core_regs(target); + register_cache_invalidate(cortex_m3->armv7m.core_cache); while (timeout < 100) { 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); @@ -538,7 +622,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; } @@ -550,6 +634,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) { @@ -566,30 +651,50 @@ 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->arm.pc; 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); + /* if we halted last time due to a bkpt instruction + * then we have to manually step over it, otherwise + * the core will break again */ + + if (!breakpoint_find(target, buf_get_u32(r->value, 0, 32)) + && !debug_execution) + { + armv7m_maybe_skip_bkpt_inst(target, NULL); + } + + resume_pc = buf_get_u32(r->value, 0, 32); armv7m_restore_context(target); @@ -614,7 +719,8 @@ static int cortex_m3_resume(struct target *target, int current, target->debug_reason = DBG_REASON_NOTHALTED; /* registers are now invalid */ - armv7m_invalidate_core_regs(target); + register_cache_invalidate(armv7m->core_cache); + if (!debug_execution) { target->state = TARGET_RUNNING; @@ -639,6 +745,8 @@ 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->arm.pc; + bool bkpt_inst_found = false; if (target->state != TARGET_HALTED) { @@ -648,39 +756,52 @@ 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); } + armv7m_maybe_skip_bkpt_inst(target, &bkpt_inst_found); + target->debug_reason = DBG_REASON_SINGLESTEP; armv7m_restore_context(target); target_call_event_callbacks(target, TARGET_EVENT_RESUMED); - /* set step and clear halt */ - cortex_m3_write_debug_halt_mask(target, C_STEP, C_HALT); + /* if no bkpt instruction is found at pc then we can perform + * a normal step, otherwise we have to manually step over the bkpt + * instruction - as such simulate a step */ + if (bkpt_inst_found == false) + { + /* set step and clear halt */ + cortex_m3_write_debug_halt_mask(target, C_STEP, C_HALT); + } + mem_ap_read_atomic_u32(swjdp, DCB_DHCSR, &cortex_m3->dcb_dhcsr); /* registers are now invalid */ - armv7m_invalidate_core_regs(target); + register_cache_invalidate(cortex_m3->armv7m.core_cache); 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; } @@ -714,21 +835,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); } /* @@ -809,7 +934,7 @@ static int cortex_m3_assert_reset(struct target *target) target->state = TARGET_RESET; jtag_add_sleep(50000); - armv7m_invalidate_core_regs(target); + register_cache_invalidate(cortex_m3->armv7m.core_cache); if (target->reset_halt) { @@ -858,9 +983,8 @@ cortex_m3_set_breakpoint(struct target *target, struct breakpoint *breakpoint) fp_num++; if (fp_num >= cortex_m3->fp_num_code) { - LOG_DEBUG("ERROR Can not find free FP Comparator"); - LOG_WARNING("ERROR Can not find free FP Comparator"); - exit(-1); + LOG_ERROR("Can not find free FPB Comparator!"); + return ERROR_FAIL; } breakpoint->set = fp_num + 1; hilo = (breakpoint->address & 0x2) ? FPCR_REPLACE_BKPT_HIGH : FPCR_REPLACE_BKPT_LOW; @@ -877,16 +1001,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)", @@ -949,7 +1082,7 @@ cortex_m3_unset_breakpoint(struct target *target, struct breakpoint *breakpoint) } } } - breakpoint->set = 0; + breakpoint->set = false; return ERROR_OK; } @@ -1128,7 +1261,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; } @@ -1138,13 +1271,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?"); @@ -1221,7 +1347,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; } @@ -1319,8 +1445,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); @@ -1372,16 +1501,11 @@ static int cortex_m3_read_memory(struct target *target, uint32_t address, { struct armv7m_common *armv7m = target_to_armv7m(target); struct swjdp_common *swjdp = &armv7m->swjdp_info; - int retval; - - /* sanitize arguments */ - if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) - return ERROR_INVALID_ARGUMENTS; + int retval = ERROR_INVALID_ARGUMENTS; /* cortex_m3 handles unaligned memory access */ - - switch (size) - { + if (count && buffer) { + switch (size) { case 4: retval = mem_ap_read_buf_u32(swjdp, buffer, 4 * count, address); break; @@ -1391,9 +1515,7 @@ static int cortex_m3_read_memory(struct target *target, uint32_t address, case 1: retval = mem_ap_read_buf_u8(swjdp, buffer, count, address); break; - default: - LOG_ERROR("BUG: we shouldn't get here"); - exit(-1); + } } return retval; @@ -1404,14 +1526,10 @@ static int cortex_m3_write_memory(struct target *target, uint32_t address, { struct armv7m_common *armv7m = target_to_armv7m(target); struct swjdp_common *swjdp = &armv7m->swjdp_info; - int retval; + int retval = ERROR_INVALID_ARGUMENTS; - /* sanitize arguments */ - if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) - return ERROR_INVALID_ARGUMENTS; - - switch (size) - { + if (count && buffer) { + switch (size) { case 4: retval = mem_ap_write_buf_u32(swjdp, buffer, 4 * count, address); break; @@ -1421,9 +1539,7 @@ static int cortex_m3_write_memory(struct target *target, uint32_t address, case 1: retval = mem_ap_write_buf_u8(swjdp, buffer, count, address); break; - default: - LOG_ERROR("BUG: we shouldn't get here"); - exit(-1); + } } return retval; @@ -1476,6 +1592,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 */ }; @@ -1492,7 +1611,10 @@ static struct dwt_reg dwt_comp[] = { #undef DWT_COMPARATOR }; -static int dwt_reg_type = -1; +static const struct reg_arch_type dwt_reg_type = { + .get = cortex_m3_dwt_get_reg, + .set = cortex_m3_dwt_set_reg, +}; static void cortex_m3_dwt_addreg(struct target *t, struct reg *r, struct dwt_reg *d) @@ -1509,7 +1631,7 @@ cortex_m3_dwt_addreg(struct target *t, struct reg *r, struct dwt_reg *d) r->size = d->size; r->value = &state->value; r->arch_info = state; - r->arch_type = dwt_reg_type; + r->type = &dwt_reg_type; } static void @@ -1526,10 +1648,6 @@ cortex_m3_dwt_setup(struct cortex_m3_common *cm3, struct target *target) return; } - if (dwt_reg_type < 0) - dwt_reg_type = register_reg_arch_type(cortex_m3_dwt_get_reg, - cortex_m3_dwt_set_reg); - cm3->dwt_num_comp = (dwtcr >> 28) & 0xF; cm3->dwt_comp_available = cm3->dwt_num_comp; cm3->dwt_comparator_list = calloc(cm3->dwt_num_comp, @@ -1603,7 +1721,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%" PRId8 "p%" PRId8 " processor detected", + (uint8_t)((cpuid >> 20) & 0xf), (uint8_t)((cpuid >> 0) & 0xf)); LOG_DEBUG("cpuid: 0x%8.8" PRIx32 "", cpuid); /* NOTE: FPB and DWT are both optional. */ @@ -1625,6 +1744,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; @@ -1719,12 +1844,11 @@ static int cortex_m3_init_arch_info(struct target *target, cortex_m3->jtag_info.tap = tap; cortex_m3->jtag_info.scann_size = 4; - armv7m->swjdp_info.dp_select_value = -1; - armv7m->swjdp_info.ap_csw_value = -1; - armv7m->swjdp_info.ap_tar_value = -1; + /* Leave (only) generic DAP stuff for debugport_init(); */ armv7m->swjdp_info.jtag_info = &cortex_m3->jtag_info; armv7m->swjdp_info.memaccess_tck = 8; - armv7m->swjdp_info.tar_autoincr_block = (1 << 12); /* Cortex-M3 has 4096 bytes autoincrement range */ + /* Cortex-M3 has 4096 bytes autoincrement range */ + armv7m->swjdp_info.tar_autoincr_block = (1 << 12); /* register arch-specific functions */ armv7m->examine_debug_reason = cortex_m3_examine_debug_reason; @@ -1784,26 +1908,26 @@ static int cortex_m3_verify_pointer(struct command_context *cmd_ctx, COMMAND_HANDLER(handle_cortex_m3_disassemble_command) { int retval; - struct target *target = get_current_target(cmd_ctx); + struct target *target = get_current_target(CMD_CTX); struct cortex_m3_common *cortex_m3 = target_to_cm3(target); uint32_t address; unsigned long count = 1; struct arm_instruction cur_instruction; - retval = cortex_m3_verify_pointer(cmd_ctx, cortex_m3); + retval = cortex_m3_verify_pointer(CMD_CTX, cortex_m3); if (retval != ERROR_OK) return retval; errno = 0; - switch (argc) { + switch (CMD_ARGC) { case 2: - COMMAND_PARSE_NUMBER(ulong, args[1], count); + COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[1], count); /* FALL THROUGH */ case 1: - COMMAND_PARSE_NUMBER(u32, args[0], address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); break; default: - command_print(cmd_ctx, + command_print(CMD_CTX, "usage: cortex_m3 disassemble
[]"); return ERROR_OK; } @@ -1812,7 +1936,7 @@ COMMAND_HANDLER(handle_cortex_m3_disassemble_command) retval = thumb2_opcode(target, address, &cur_instruction); if (retval != ERROR_OK) return retval; - command_print(cmd_ctx, "%s", cur_instruction.text); + command_print(CMD_CTX, "%s", cur_instruction.text); address += cur_instruction.instruction_size; } @@ -1835,121 +1959,138 @@ static const struct { COMMAND_HANDLER(handle_cortex_m3_vector_catch_command) { - struct target *target = get_current_target(cmd_ctx); + struct target *target = get_current_target(CMD_CTX); struct cortex_m3_common *cortex_m3 = target_to_cm3(target); struct armv7m_common *armv7m = &cortex_m3->armv7m; struct swjdp_common *swjdp = &armv7m->swjdp_info; uint32_t demcr = 0; int retval; - int i; - retval = cortex_m3_verify_pointer(cmd_ctx, cortex_m3); + retval = cortex_m3_verify_pointer(CMD_CTX, cortex_m3); if (retval != ERROR_OK) return retval; mem_ap_read_atomic_u32(swjdp, DCB_DEMCR, &demcr); - if (argc > 0) { + if (CMD_ARGC > 0) { unsigned catch = 0; - if (argc == 1) { - if (strcmp(args[0], "all") == 0) { + if (CMD_ARGC == 1) { + if (strcmp(CMD_ARGV[0], "all") == 0) { catch = VC_HARDERR | VC_INTERR | VC_BUSERR | VC_STATERR | VC_CHKERR | VC_NOCPERR | VC_MMERR | VC_CORERESET; goto write; - } else if (strcmp(args[0], "none") == 0) { + } else if (strcmp(CMD_ARGV[0], "none") == 0) { goto write; } } - while (argc-- > 0) { + while (CMD_ARGC-- > 0) { + unsigned i; for (i = 0; i < ARRAY_SIZE(vec_ids); i++) { - if (strcmp(args[argc], vec_ids[i].name) != 0) + if (strcmp(CMD_ARGV[CMD_ARGC], vec_ids[i].name) != 0) continue; catch |= vec_ids[i].mask; break; } if (i == ARRAY_SIZE(vec_ids)) { - LOG_ERROR("No CM3 vector '%s'", args[argc]); + LOG_ERROR("No CM3 vector '%s'", CMD_ARGV[CMD_ARGC]); return ERROR_INVALID_ARGUMENTS; } } 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 (i = 0; i < ARRAY_SIZE(vec_ids); i++) - command_print(cmd_ctx, "%9s: %s", vec_ids[i].name, + for (unsigned i = 0; i < ARRAY_SIZE(vec_ids); i++) + { + command_print(CMD_CTX, "%9s: %s", vec_ids[i].name, (demcr & vec_ids[i].mask) ? "catch" : "ignore"); + } return ERROR_OK; } COMMAND_HANDLER(handle_cortex_m3_mask_interrupts_command) { - struct target *target = get_current_target(cmd_ctx); + struct target *target = get_current_target(CMD_CTX); struct cortex_m3_common *cortex_m3 = target_to_cm3(target); int retval; - retval = cortex_m3_verify_pointer(cmd_ctx, cortex_m3); + retval = cortex_m3_verify_pointer(CMD_CTX, cortex_m3); if (retval != ERROR_OK) return retval; if (target->state != TARGET_HALTED) { - command_print(cmd_ctx, "target must be stopped for \"%s\" command", CMD_NAME); + command_print(CMD_CTX, "target must be stopped for \"%s\" command", CMD_NAME); return ERROR_OK; } - if (argc > 0) + if (CMD_ARGC > 0) { - if (!strcmp(args[0], "on")) - { - cortex_m3_write_debug_halt_mask(target, C_HALT | C_MASKINTS, 0); - } - else if (!strcmp(args[0], "off")) - { - cortex_m3_write_debug_halt_mask(target, C_HALT, C_MASKINTS); - } - else - { - command_print(cmd_ctx, "usage: cortex_m3 maskisr ['on'|'off']"); - } + bool enable; + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable); + uint32_t mask_on = C_HALT | (enable ? C_MASKINTS : 0); + uint32_t mask_off = enable ? 0 : C_MASKINTS; + cortex_m3_write_debug_halt_mask(target, mask_on, mask_off); } - command_print(cmd_ctx, "cortex_m3 interrupt mask %s", + command_print(CMD_CTX, "cortex_m3 interrupt mask %s", (cortex_m3->dcb_dhcsr & C_MASKINTS) ? "on" : "off"); return ERROR_OK; } -static int cortex_m3_register_commands(struct command_context *cmd_ctx) -{ - int retval; - command_t *cortex_m3_cmd; - - retval = armv7m_register_commands(cmd_ctx); - - cortex_m3_cmd = register_command(cmd_ctx, NULL, "cortex_m3", - NULL, COMMAND_ANY, "cortex_m3 specific commands"); - - register_command(cmd_ctx, cortex_m3_cmd, "disassemble", - handle_cortex_m3_disassemble_command, COMMAND_EXEC, - "disassemble Thumb2 instructions
[]"); - register_command(cmd_ctx, cortex_m3_cmd, "maskisr", - handle_cortex_m3_mask_interrupts_command, COMMAND_EXEC, - "mask cortex_m3 interrupts ['on'|'off']"); - register_command(cmd_ctx, cortex_m3_cmd, "vector_catch", - handle_cortex_m3_vector_catch_command, COMMAND_EXEC, - "catch hardware vectors ['all'|'none'|]"); - - return retval; -} +static const struct command_registration cortex_m3_exec_command_handlers[] = { + { + .name = "disassemble", + .handler = handle_cortex_m3_disassemble_command, + .mode = COMMAND_EXEC, + .help = "disassemble Thumb2 instructions", + .usage = "address [count]", + }, + { + .name = "maskisr", + .handler = handle_cortex_m3_mask_interrupts_command, + .mode = COMMAND_EXEC, + .help = "mask cortex_m3 interrupts", + .usage = "['on'|'off']", + }, + { + .name = "vector_catch", + .handler = handle_cortex_m3_vector_catch_command, + .mode = COMMAND_EXEC, + .help = "configure hardware vectors to trigger debug entry", + .usage = "['all'|'none'|('bus_err'|'chk_err'|...)*]", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration cortex_m3_command_handlers[] = { + { + .chain = armv7m_command_handlers, + }, + { + .name = "cortex_m3", + .mode = COMMAND_EXEC, + .help = "Cortex-M3 command group", + .chain = cortex_m3_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; struct target_type cortexm3_target = { @@ -1983,7 +2124,7 @@ struct target_type cortexm3_target = .add_watchpoint = cortex_m3_add_watchpoint, .remove_watchpoint = cortex_m3_remove_watchpoint, - .register_commands = cortex_m3_register_commands, + .commands = cortex_m3_command_handlers, .target_create = cortex_m3_target_create, .init_target = cortex_m3_init_target, .examine = cortex_m3_examine,