/* 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
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 */
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 */
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;
- /* FIXME handling of DEMCR clobbers vector_catch config ... */
+ /* 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);
/* clear any interrupt masking */
cortex_m3_write_debug_halt_mask(target, 0, C_MASKINTS);
- /* Enable trace and DWT; trap hard and bus faults.
- *
- * REVISIT why trap those two? And why trash the vector_catch
- * config, instead of preserving it? Catching HARDERR and BUSERR
- * will interfere with code that handles those itself...
- */
- mem_ap_write_u32(swjdp, DCB_DEMCR, TRCENA | VC_HARDERR | VC_BUSERR);
-
- /* Monitor bus faults as such (instead of as generic HARDERR), but
- * leave memory management and usage faults disabled.
+ /* Enable features controlled by ITM and DWT blocks, and catch only
+ * the vectors we were told to pay attention to.
*
- * REVISIT setting BUSFAULTENA interferes with code which relies
- * on the default setting. Why do it?
+ * 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, NVIC_SHCSR, SHCSR_BUSFAULTENA);
+ 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...
target_write_u32(target, dwt_list[i].dwt_comparator_address + 8,
dwt_list[i].function);
}
- swjdp_transaction_endcheck(swjdp);
+ jtagdp_transaction_endcheck(swjdp);
register_cache_invalidate(cortex_m3->armv7m.core_cache);
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;
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;
{
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;
}
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)
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 */
uint32_t dcb_dhcsr = 0;
int retval, timeout = 0;
- /* Enter debug state on reset; see end_reset_event() */
+ /* Enter debug state on reset; restore DEMCR in endreset_event() */
mem_ap_write_u32(swjdp, DCB_DEMCR,
TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET);
/* set any pending breakpoints */
while (breakpoint)
{
- if (breakpoint->set == 0)
+ if (!breakpoint->set)
cortex_m3_set_breakpoint(target, breakpoint);
breakpoint = breakpoint->next;
}
}
/* current = 1: continue on current pc, otherwise continue at <address> */
- r = armv7m->core_cache->reg_list + 15;
+ r = armv7m->arm.pc;
if (!current)
{
buf_set_u32(r->value, 0, 32, address);
r->valid = true;
}
+ /* 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);
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;
+ struct reg *pc = armv7m->arm.pc;
+ bool bkpt_inst_found = false;
if (target->state != TARGET_HALTED)
{
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 */
LOG_DEBUG("target stepped dcb_dhcsr = 0x%" PRIx32
" nvic_icsr = 0x%" PRIx32,
cortex_m3->dcb_dhcsr, cortex_m3->nvic_icsr);
+
return ERROR_OK;
}
/* 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() */
+ /* 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);
}
else if (breakpoint->type == BKPT_SOFT)
{
uint8_t code[4];
+
+ /* 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));
- if ((retval = target_read_memory(target, breakpoint->address & 0xFFFFFFFE, breakpoint->length, 1, breakpoint->orig_instr)) != ERROR_OK)
- {
+ 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)",
}
}
}
- breakpoint->set = 0;
+ breakpoint->set = false;
return ERROR_OK;
}
target_write_u32(target, comparator->dwt_comparator_address + 8,
comparator->function);
- watchpoint->set = 0;
+ watchpoint->set = false;
return ERROR_OK;
}
/* set any pending watchpoints */
while (watchpoint)
{
- if (watchpoint->set == 0)
+ if (!watchpoint->set)
cortex_m3_set_watchpoint(target, watchpoint);
watchpoint = watchpoint->next;
}
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. */
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;
}
}
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++)
static const struct command_registration cortex_m3_exec_command_handlers[] = {
{
.name = "disassemble",
- .handler = &handle_cortex_m3_disassemble_command,
+ .handler = handle_cortex_m3_disassemble_command,
.mode = COMMAND_EXEC,
.help = "disassemble Thumb2 instructions",
- .usage = "<address> [<count>]",
+ .usage = "address [count]",
},
{
.name = "maskisr",
- .handler = &handle_cortex_m3_mask_interrupts_command,
+ .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,
+ .handler = handle_cortex_m3_vector_catch_command,
.mode = COMMAND_EXEC,
- .help = "catch hardware vectors",
- .usage = "['all'|'none'|<list>]",
+ .help = "configure hardware vectors to trigger debug entry",
+ .usage = "['all'|'none'|('bus_err'|'chk_err'|...)*]",
},
COMMAND_REGISTRATION_DONE
};