static int dpmv8_write_dcc(struct armv8_common *armv8, uint32_t data)
{
- LOG_DEBUG("write DCC 0x%08" PRIx32, data);
return mem_ap_write_u32(armv8->debug_ap,
armv8->debug_base + CPUV8_DBG_DTRRX, data);
}
static int dpmv8_write_dcc_64(struct armv8_common *armv8, uint64_t data)
{
int ret;
- LOG_DEBUG("write DCC Low word 0x%08" PRIx32, (unsigned)data);
- LOG_DEBUG("write DCC High word 0x%08" PRIx32, (unsigned)(data >> 32));
ret = mem_ap_write_u32(armv8->debug_ap,
armv8->debug_base + CPUV8_DBG_DTRRX, data);
- ret += mem_ap_write_u32(armv8->debug_ap,
+ if (ret == ERROR_OK)
+ ret = mem_ap_write_u32(armv8->debug_ap,
armv8->debug_base + CPUV8_DBG_DTRTX, data >> 32);
return ret;
}
data);
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("read DCC 0x%08" PRIx32, *data);
if (dscr_p)
*dscr_p = dscr;
if (retval != ERROR_OK)
return retval;
if (timeval_ms() > then + 1000) {
- LOG_ERROR("Timeout waiting for read dcc");
+ LOG_ERROR("Timeout waiting for DTR_TX_FULL, dscr = 0x%08" PRIx32, dscr);
return ERROR_FAIL;
}
}
return retval;
*data = *(uint32_t *)data | (uint64_t)higher << 32;
- LOG_DEBUG("read DCC 0x%16.16" PRIx64, *data);
if (dscr_p)
*dscr_p = dscr;
uint32_t dscr;
int retval;
- /* set up invariant: INSTR_COMP is set after ever DPM operation */
+ /* set up invariant: ITE is set after ever DPM operation */
long long then = timeval_ms();
for (;; ) {
retval = mem_ap_read_atomic_u32(armv8->debug_ap,
armv8->debug_base + CPUV8_DBG_DTRRX, &dscr);
if (retval != ERROR_OK)
return retval;
-
- /* Clear sticky error */
- retval = mem_ap_write_u32(armv8->debug_ap,
- armv8->debug_base + CPUV8_DBG_DRCR, DRCR_CSE);
- if (retval != ERROR_OK)
- return retval;
}
return retval;
uint32_t opcode, uint32_t *p_dscr)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
- uint32_t dscr = DSCR_ITE;
+ uint32_t dscr = dpm->dscr;
int retval;
- LOG_DEBUG("exec opcode 0x%08" PRIx32, opcode);
-
if (p_dscr)
dscr = *p_dscr;
if (dscr & DSCR_ERR) {
LOG_ERROR("Opcode 0x%08"PRIx32", DSCR.ERR=1, DSCR.EL=%i", opcode, dpm->last_el);
- /* clear the sticky error condition */
- mem_ap_write_atomic_u32(armv8->debug_ap,
- armv8->debug_base + CPUV8_DBG_DRCR, DRCR_CSE);
+ armv8_dpm_handle_exception(dpm);
retval = ERROR_FAIL;
}
uint32_t opcode, uint64_t data)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
- uint32_t dscr = DSCR_ITE;
int retval;
- retval = dpmv8_write_dcc_64(armv8, data);
- if (retval != ERROR_OK)
- return retval;
+ if (dpm->arm->core_state != ARM_STATE_AARCH64)
+ return dpmv8_instr_write_data_r0(dpm, opcode, data);
- retval = dpmv8_exec_opcode(dpm, ARMV8_MRS(SYSTEM_DBG_DBGDTR_EL0, 0), &dscr);
- if (retval != ERROR_OK)
- return retval;
+ /* transfer data from DCC to R0 */
+ retval = dpmv8_write_dcc_64(armv8, data);
+ if (retval == ERROR_OK)
+ retval = dpmv8_exec_opcode(dpm, ARMV8_MRS(SYSTEM_DBG_DBGDTR_EL0, 0), &dpm->dscr);
/* then the opcode, taking data from R0 */
- return dpmv8_exec_opcode(dpm, opcode, &dscr);
+ if (retval == ERROR_OK)
+ retval = dpmv8_exec_opcode(dpm, opcode, &dpm->dscr);
+
+ return retval;
}
static int dpmv8_instr_cpsr_sync(struct arm_dpm *dpm)
{
+ int retval;
struct armv8_common *armv8 = dpm->arm->arch_info;
+
/* "Prefetch flush" after modifying execution status in CPSR */
- return dpmv8_exec_opcode(dpm, armv8_opcode(armv8, ARMV8_OPC_DSB_SY), NULL);
+ retval = dpmv8_exec_opcode(dpm, armv8_opcode(armv8, ARMV8_OPC_DSB_SY), &dpm->dscr);
+ if (retval == ERROR_OK)
+ dpmv8_exec_opcode(dpm, armv8_opcode(armv8, ARMV8_OPC_ISB_SY), &dpm->dscr);
+ return retval;
}
static int dpmv8_instr_read_data_dcc(struct arm_dpm *dpm,
uint32_t opcode, uint32_t *data)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
- uint32_t dscr = DSCR_ITE;
int retval;
/* the opcode, writing data to DCC */
- retval = dpmv8_exec_opcode(dpm, opcode, &dscr);
+ retval = dpmv8_exec_opcode(dpm, opcode, &dpm->dscr);
if (retval != ERROR_OK)
return retval;
- return dpmv8_read_dcc(armv8, data, &dscr);
+ return dpmv8_read_dcc(armv8, data, &dpm->dscr);
}
static int dpmv8_instr_read_data_dcc_64(struct arm_dpm *dpm,
uint32_t opcode, uint64_t *data)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
- uint32_t dscr = DSCR_ITE;
int retval;
/* the opcode, writing data to DCC */
- retval = dpmv8_exec_opcode(dpm, opcode, &dscr);
+ retval = dpmv8_exec_opcode(dpm, opcode, &dpm->dscr);
if (retval != ERROR_OK)
return retval;
- return dpmv8_read_dcc_64(armv8, data, &dscr);
+ return dpmv8_read_dcc_64(armv8, data, &dpm->dscr);
}
static int dpmv8_instr_read_data_r0(struct arm_dpm *dpm,
uint32_t opcode, uint32_t *data)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
- uint32_t dscr = DSCR_ITE;
int retval;
/* the opcode, writing data to R0 */
- retval = dpmv8_exec_opcode(dpm, opcode, &dscr);
+ retval = dpmv8_exec_opcode(dpm, opcode, &dpm->dscr);
if (retval != ERROR_OK)
return retval;
/* write R0 to DCC */
- retval = dpmv8_exec_opcode(dpm, armv8_opcode(armv8, WRITE_REG_DTRTX), &dscr);
+ retval = dpmv8_exec_opcode(dpm, armv8_opcode(armv8, WRITE_REG_DTRTX), &dpm->dscr);
if (retval != ERROR_OK)
return retval;
- return dpmv8_read_dcc(armv8, data, &dscr);
+ return dpmv8_read_dcc(armv8, data, &dpm->dscr);
}
static int dpmv8_instr_read_data_r0_64(struct arm_dpm *dpm,
uint32_t opcode, uint64_t *data)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
- uint32_t dscr = DSCR_ITE;
int retval;
+ if (dpm->arm->core_state != ARM_STATE_AARCH64) {
+ uint32_t tmp;
+ retval = dpmv8_instr_read_data_r0(dpm, opcode, &tmp);
+ if (retval == ERROR_OK)
+ *data = tmp;
+ return retval;
+ }
+
/* the opcode, writing data to R0 */
- retval = dpmv8_exec_opcode(dpm, opcode, &dscr);
+ retval = dpmv8_exec_opcode(dpm, opcode, &dpm->dscr);
if (retval != ERROR_OK)
return retval;
/* write R0 to DCC */
- retval = dpmv8_exec_opcode(dpm, ARMV8_MSR_GP(SYSTEM_DBG_DBGDTR_EL0, 0), &dscr);
+ retval = dpmv8_exec_opcode(dpm, ARMV8_MSR_GP(SYSTEM_DBG_DBGDTR_EL0, 0), &dpm->dscr);
if (retval != ERROR_OK)
return retval;
- return dpmv8_read_dcc_64(armv8, data, &dscr);
+ return dpmv8_read_dcc_64(armv8, data, &dpm->dscr);
}
#if 0
return retval;
}
-static int dpmv8_mrs(struct target *target, uint32_t op0,
- uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
- uint32_t *value)
-{
- struct arm *arm = target_to_arm(target);
- struct arm_dpm *dpm = arm->dpm;
- int retval;
- uint32_t op_code;
-
- retval = dpm->prepare(dpm);
- if (retval != ERROR_OK)
- return retval;
- op_code = ((op0 & 0x3) << 19 | (op1 & 0x7) << 16 | (CRn & 0xF) << 12 |\
- (CRm & 0xF) << 8 | (op2 & 0x7) << 5);
- op_code >>= 5;
- LOG_DEBUG("MRS p%d, %d, r0, c%d, c%d, %d", (int)op0,
- (int) op1, (int) CRn,
- (int) CRm, (int) op2);
- /* read coprocessor register into R0; return via DCC */
- retval = dpm->instr_read_data_r0(dpm,
- ARMV8_MRS(op_code, 0),
- value);
-
- /* (void) */ dpm->finish(dpm);
- return retval;
-}
-
-static int dpmv8_msr(struct target *target, uint32_t op0,
- uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
- uint32_t value)
-{
- struct arm *arm = target_to_arm(target);
- struct arm_dpm *dpm = arm->dpm;
- int retval;
- uint32_t op_code;
-
- retval = dpm->prepare(dpm);
- if (retval != ERROR_OK)
- return retval;
-
- op_code = ((op0 & 0x3) << 19 | (op1 & 0x7) << 16 | (CRn & 0xF) << 12 |\
- (CRm & 0xF) << 8 | (op2 & 0x7) << 5);
- op_code >>= 5;
- LOG_DEBUG("MSR p%d, %d, r0, c%d, c%d, %d", (int)op0,
- (int) op1, (int) CRn,
- (int) CRm, (int) op2);
-
- /* read DCC into r0; then write coprocessor register from R0 */
- retval = dpm->instr_write_data_r0(dpm,
- ARMV8_MSR_GP(op_code, 0),
- value);
-
- /* (void) */ dpm->finish(dpm);
- return retval;
-}
-
/*----------------------------------------------------------------------*/
/*
if (target_el > dpm->last_el) {
retval = dpm->instr_execute(dpm,
armv8_opcode(armv8, ARMV8_OPC_DCPS) | target_el);
+
+ /* DCPS clobbers registers just like an exception taken */
+ armv8_dpm_handle_exception(dpm);
} else {
core_state = armv8_dpm_get_core_state(dpm);
if (core_state != ARM_STATE_AARCH64) {
/* ?? */
break;
default:
- LOG_DEBUG("Unknow core_state");
+ LOG_DEBUG("Unknown core_state");
break;
}
dpm->wp_pc = addr;
}
+/*
+ * Handle exceptions taken in debug state. This happens mostly for memory
+ * accesses that violated a MMU policy. Taking an exception while in debug
+ * state clobbers certain state registers on the target exception level.
+ * Just mark those registers dirty so that they get restored on resume.
+ * This works both for Aarch32 and Aarch64 states.
+ *
+ * This function must not perform any actions that trigger another exception
+ * or a recursion will happen.
+ */
+void armv8_dpm_handle_exception(struct arm_dpm *dpm)
+{
+ struct armv8_common *armv8 = dpm->arm->arch_info;
+ struct reg_cache *cache = dpm->arm->core_cache;
+ enum arm_state core_state;
+ uint64_t dlr;
+ uint32_t dspsr;
+ unsigned int el;
+
+ static const int clobbered_regs_by_el[3][5] = {
+ { ARMV8_PC, ARMV8_xPSR, ARMV8_ELR_EL1, ARMV8_ESR_EL1, ARMV8_SPSR_EL1 },
+ { ARMV8_PC, ARMV8_xPSR, ARMV8_ELR_EL2, ARMV8_ESR_EL2, ARMV8_SPSR_EL2 },
+ { ARMV8_PC, ARMV8_xPSR, ARMV8_ELR_EL3, ARMV8_ESR_EL3, ARMV8_SPSR_EL3 },
+ };
+
+ el = (dpm->dscr >> 8) & 3;
+
+ /* safety check, must not happen since EL0 cannot be a target for an exception */
+ if (el < SYSTEM_CUREL_EL1 || el > SYSTEM_CUREL_EL3) {
+ LOG_ERROR("%s: EL %i is invalid, DSCR corrupted?", __func__, el);
+ return;
+ }
+
+ /* Clear sticky error */
+ mem_ap_write_u32(armv8->debug_ap,
+ armv8->debug_base + CPUV8_DBG_DRCR, DRCR_CSE);
+
+ armv8->read_reg_u64(armv8, ARMV8_xPSR, &dlr);
+ dspsr = dlr;
+ armv8->read_reg_u64(armv8, ARMV8_PC, &dlr);
+
+ LOG_DEBUG("Exception taken to EL %i, DLR=0x%016"PRIx64" DSPSR=0x%08"PRIx32,
+ el, dlr, dspsr);
+
+ /* mark all clobbered registers as dirty */
+ for (int i = 0; i < 5; i++)
+ cache->reg_list[clobbered_regs_by_el[el-1][i]].dirty = true;
+
+ /*
+ * re-evaluate the core state, we might be in Aarch64 state now
+ * we rely on dpm->dscr being up-to-date
+ */
+ core_state = armv8_dpm_get_core_state(dpm);
+ armv8_select_opcodes(armv8, core_state == ARM_STATE_AARCH64);
+ armv8_select_reg_access(armv8, core_state == ARM_STATE_AARCH64);
+}
+
/*----------------------------------------------------------------------*/
/*
/* coprocessor access setup */
arm->mrc = dpmv8_mrc;
arm->mcr = dpmv8_mcr;
- arm->mrs = dpmv8_mrs;
- arm->msr = dpmv8_msr;
dpm->prepare = dpmv8_dpm_prepare;
dpm->finish = dpmv8_dpm_finish;