aarch64: introduce dpm extension for ARMv8
Add or move ARMv8 related dpm function to their own source module Change-Id: Id93d50be0b8635bd40ddb2a74fe8746ff840d736 Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "arm.h"
|
||||
#include "arm_dpm.h"
|
||||
#include "armv8_dpm.h"
|
||||
#include <jtag/jtag.h>
|
||||
#include "register.h"
|
||||
#include "breakpoints.h"
|
||||
@@ -130,11 +131,11 @@ int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int dpm_read_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
/* just read the register -- rely on the core mode being right */
|
||||
static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
{
|
||||
uint32_t value;
|
||||
int retval = ERROR_FAIL;
|
||||
int retval;
|
||||
|
||||
switch (regnum) {
|
||||
case 0 ... 14:
|
||||
@@ -165,8 +166,8 @@ static int dpm_read_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
/* core-specific ... ? */
|
||||
LOG_WARNING("Jazelle PC adjustment unknown");
|
||||
break;
|
||||
case ARM_STATE_AARCH64:
|
||||
LOG_ERROR("AARCH64: 32bit read requested");
|
||||
default:
|
||||
LOG_WARNING("unknow core state");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -190,57 +191,10 @@ static int dpm_read_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int dpm_read_reg64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
/* just write the register -- rely on the core mode being right */
|
||||
static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
{
|
||||
uint64_t value;
|
||||
uint32_t i;
|
||||
int retval = ERROR_FAIL;
|
||||
|
||||
switch (regnum) {
|
||||
case 0 ... 30:
|
||||
i = 0xd5130400 + regnum; /* msr dbgdtr_el0,reg */
|
||||
retval = dpm->instr_read_data_dcc_64(dpm, i, &value);
|
||||
break;
|
||||
case 31: /* SP */
|
||||
i = 0x910003e0;
|
||||
retval = dpm->instr_read_data_r0_64(dpm, i, &value);
|
||||
break;
|
||||
case 32: /* PC */
|
||||
i = 0xd53b4520;
|
||||
retval = dpm->instr_read_data_r0_64(dpm, i, &value);
|
||||
break;
|
||||
case 33: /* CPSR */
|
||||
i = 0xd53b4500;
|
||||
retval = dpm->instr_read_data_r0_64(dpm, i, &value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (retval == ERROR_OK) {
|
||||
buf_set_u64(r->value, 0, 64, value);
|
||||
r->valid = true;
|
||||
r->dirty = false;
|
||||
LOG_DEBUG("READ: %s, %16.16llx", r->name, (long long)value);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* just read the register -- rely on the core mode being right */
|
||||
static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
{
|
||||
if (r->size == 64)
|
||||
return dpm_read_reg64(dpm, r, regnum);
|
||||
else
|
||||
return dpm_read_reg32(dpm, r, regnum);
|
||||
}
|
||||
|
||||
static int dpm_write_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
{
|
||||
int retval = ERROR_FAIL;
|
||||
int retval;
|
||||
uint32_t value = buf_get_u32(r->value, 0, 32);
|
||||
|
||||
switch (regnum) {
|
||||
@@ -290,51 +244,19 @@ static int dpm_write_pc_core_state(struct arm_dpm *dpm, struct reg *r)
|
||||
return dpm->instr_write_data_r0(dpm, ARMV4_5_BX(0), value);
|
||||
}
|
||||
|
||||
static int dpm_write_reg64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
{
|
||||
int retval = ERROR_FAIL;
|
||||
uint32_t i;
|
||||
uint64_t value = buf_get_u64(r->value, 0, 64);
|
||||
|
||||
switch (regnum) {
|
||||
case 0 ... 30:
|
||||
i = 0xd5330400 + regnum;
|
||||
retval = dpm->instr_write_data_dcc_64(dpm, i, value);
|
||||
break;
|
||||
case 32: /* PC */
|
||||
i = 0xd51b4520;
|
||||
retval = dpm->instr_write_data_r0_64(dpm, i, value);
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG("register %s (%16.16llx) not defined", r->name,
|
||||
(unsigned long long)value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (retval == ERROR_OK) {
|
||||
r->dirty = false;
|
||||
LOG_DEBUG("WRITE: %s, %16.16llx", r->name, (unsigned long long)value);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* just write the register -- rely on the core mode being right */
|
||||
static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||
{
|
||||
if (r->size == 64)
|
||||
return dpm_write_reg64(dpm, r, regnum);
|
||||
else
|
||||
return dpm_write_reg32(dpm, r, regnum);
|
||||
}
|
||||
|
||||
static int arm_dpm_read_current_registers_i(struct arm_dpm *dpm)
|
||||
/**
|
||||
* Read basic registers of the the current context: R0 to R15, and CPSR;
|
||||
* sets the core mode (such as USR or IRQ) and state (such as ARM or Thumb).
|
||||
* In normal operation this is called on entry to halting debug state,
|
||||
* possibly after some other operations supporting restore of debug state
|
||||
* or making sure the CPU is fully idle (drain write buffer, etc).
|
||||
*/
|
||||
int arm_dpm_read_current_registers(struct arm_dpm *dpm)
|
||||
{
|
||||
struct arm *arm = dpm->arm;
|
||||
uint32_t cpsr, instr, core_regs;
|
||||
uint32_t cpsr;
|
||||
int retval;
|
||||
struct reg *r;
|
||||
enum arm_state core_state = arm->core_state;
|
||||
|
||||
retval = dpm->prepare(dpm);
|
||||
if (retval != ERROR_OK)
|
||||
@@ -349,25 +271,16 @@ static int arm_dpm_read_current_registers_i(struct arm_dpm *dpm)
|
||||
}
|
||||
r->dirty = true;
|
||||
|
||||
if (core_state == ARM_STATE_AARCH64)
|
||||
instr = 0xd53b4500; /* mrs x0, dspsr_el0 */
|
||||
else
|
||||
instr = ARMV4_5_MRS(0, 0);
|
||||
retval = dpm->instr_read_data_r0(dpm, instr, &cpsr);
|
||||
retval = dpm->instr_read_data_r0(dpm, ARMV4_5_MRS(0, 0), &cpsr);
|
||||
if (retval != ERROR_OK)
|
||||
goto fail;
|
||||
|
||||
/* update core mode and state, plus shadow mapping for R8..R14 */
|
||||
arm_set_cpsr(arm, cpsr);
|
||||
if (core_state == ARM_STATE_AARCH64)
|
||||
/* arm_set_cpsr changes core_state, restore it for now */
|
||||
arm->core_state = ARM_STATE_AARCH64;
|
||||
|
||||
core_regs = arm->core_cache->num_regs;
|
||||
|
||||
/* REVISIT we can probably avoid reading R1..R14, saving time... */
|
||||
for (unsigned i = 1; i < core_regs; i++) {
|
||||
r = dpm->arm_reg_current(arm, i);
|
||||
for (unsigned i = 1; i < 16; i++) {
|
||||
r = arm_reg_current(arm, i);
|
||||
if (r->valid)
|
||||
continue;
|
||||
|
||||
@@ -388,23 +301,6 @@ fail:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read basic registers of the the current context: R0 to R15, and CPSR;
|
||||
* sets the core mode (such as USR or IRQ) and state (such as ARM or Thumb).
|
||||
* In normal operation this is called on entry to halting debug state,
|
||||
* possibly after some other operations supporting restore of debug state
|
||||
* or making sure the CPU is fully idle (drain write buffer, etc).
|
||||
*/
|
||||
int arm_dpm_read_current_registers(struct arm_dpm *dpm)
|
||||
{
|
||||
return arm_dpm_read_current_registers_i(dpm);
|
||||
}
|
||||
|
||||
int arm_dpm_read_current_registers_64(struct arm_dpm *dpm)
|
||||
{
|
||||
return arm_dpm_read_current_registers_i(dpm);
|
||||
}
|
||||
|
||||
/* Avoid needless I/O ... leave breakpoints and watchpoints alone
|
||||
* unless they're removed, or need updating because of single-stepping
|
||||
* or running debugger code.
|
||||
@@ -453,14 +349,59 @@ done:
|
||||
|
||||
static int dpm_add_breakpoint(struct target *target, struct breakpoint *bp);
|
||||
|
||||
|
||||
static int arm_dpm_write_dirty_registers_32(struct arm_dpm *dpm)
|
||||
/**
|
||||
* Writes all modified core registers for all processor modes. In normal
|
||||
* operation this is called on exit from halting debug state.
|
||||
*
|
||||
* @param dpm: represents the processor
|
||||
* @param bpwp: true ensures breakpoints and watchpoints are set,
|
||||
* false ensures they are cleared
|
||||
*/
|
||||
int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
|
||||
{
|
||||
struct arm *arm = dpm->arm;
|
||||
struct reg_cache *cache = arm->core_cache;
|
||||
int retval;
|
||||
bool did_write;
|
||||
|
||||
retval = dpm->prepare(dpm);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
|
||||
/* If we're managing hardware breakpoints for this core, enable
|
||||
* or disable them as requested.
|
||||
*
|
||||
* REVISIT We don't yet manage them for ANY cores. Eventually
|
||||
* we should be able to assume we handle them; but until then,
|
||||
* cope with the hand-crafted breakpoint code.
|
||||
*/
|
||||
if (arm->target->type->add_breakpoint == dpm_add_breakpoint) {
|
||||
for (unsigned i = 0; i < dpm->nbp; i++) {
|
||||
struct dpm_bp *dbp = dpm->dbp + i;
|
||||
struct breakpoint *bp = dbp->bp;
|
||||
|
||||
retval = dpm_maybe_update_bpwp(dpm, bpwp, &dbp->bpwp,
|
||||
bp ? &bp->set : NULL);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable/disable watchpoints */
|
||||
for (unsigned i = 0; i < dpm->nwp; i++) {
|
||||
struct dpm_wp *dwp = dpm->dwp + i;
|
||||
struct watchpoint *wp = dwp->wp;
|
||||
|
||||
retval = dpm_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp,
|
||||
wp ? &wp->set : NULL);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* NOTE: writes to breakpoint and watchpoint registers might
|
||||
* be queued, and need (efficient/batched) flushing later.
|
||||
*/
|
||||
|
||||
/* Scan the registers until we find one that's both dirty and
|
||||
* eligible for flushing. Flush that and everything else that
|
||||
* shares the same core mode setting. Typically this won't
|
||||
@@ -564,106 +505,6 @@ static int arm_dpm_write_dirty_registers_32(struct arm_dpm *dpm)
|
||||
goto done;
|
||||
cache->reg_list[0].dirty = false;
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int arm_dpm_write_dirty_registers_64(struct arm_dpm *dpm)
|
||||
{
|
||||
struct arm *arm = dpm->arm;
|
||||
struct reg_cache *cache = arm->core_cache;
|
||||
int retval;
|
||||
|
||||
/* Scan the registers until we find one that's both dirty and
|
||||
* eligible for flushing. Flush that and everything else that
|
||||
* shares the same core mode setting. Typically this won't
|
||||
* actually find anything to do...
|
||||
*/
|
||||
|
||||
/* check everything except our scratch register R0 */
|
||||
for (unsigned i = 1; i <= 32; i++) {
|
||||
struct arm_reg *r;
|
||||
unsigned regnum;
|
||||
|
||||
if (!cache->reg_list[i].dirty)
|
||||
continue;
|
||||
|
||||
r = cache->reg_list[i].arch_info;
|
||||
regnum = r->num;
|
||||
retval = dpm_write_reg(dpm,
|
||||
&cache->reg_list[i],
|
||||
regnum);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* flush R0 -- it's *very* dirty by now */
|
||||
retval = dpm_write_reg(dpm, &cache->reg_list[0], 0);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
cache->reg_list[0].dirty = false;
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes all modified core registers for all processor modes. In normal
|
||||
* operation this is called on exit from halting debug state.
|
||||
*
|
||||
* @param dpm: represents the processor
|
||||
* @param bpwp: true ensures breakpoints and watchpoints are set,
|
||||
* false ensures they are cleared
|
||||
*/
|
||||
int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
|
||||
{
|
||||
struct arm *arm = dpm->arm;
|
||||
struct reg_cache *cache = arm->core_cache;
|
||||
int retval;
|
||||
|
||||
retval = dpm->prepare(dpm);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
|
||||
/* If we're managing hardware breakpoints for this core, enable
|
||||
* or disable them as requested.
|
||||
*
|
||||
* REVISIT We don't yet manage them for ANY cores. Eventually
|
||||
* we should be able to assume we handle them; but until then,
|
||||
* cope with the hand-crafted breakpoint code.
|
||||
*/
|
||||
if (arm->target->type->add_breakpoint == dpm_add_breakpoint) {
|
||||
for (unsigned i = 0; i < dpm->nbp; i++) {
|
||||
struct dpm_bp *dbp = dpm->dbp + i;
|
||||
struct breakpoint *bp = dbp->bp;
|
||||
|
||||
retval = dpm_maybe_update_bpwp(dpm, bpwp, &dbp->bpwp,
|
||||
bp ? &bp->set : NULL);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable/disable watchpoints */
|
||||
for (unsigned i = 0; i < dpm->nwp; i++) {
|
||||
struct dpm_wp *dwp = dpm->dwp + i;
|
||||
struct watchpoint *wp = dwp->wp;
|
||||
|
||||
retval = dpm_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp,
|
||||
wp ? &wp->set : NULL);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* NOTE: writes to breakpoint and watchpoint registers might
|
||||
* be queued, and need (efficient/batched) flushing later.
|
||||
*/
|
||||
|
||||
if (cache->reg_list[0].size == 64)
|
||||
retval = arm_dpm_write_dirty_registers_64(dpm);
|
||||
else
|
||||
retval = arm_dpm_write_dirty_registers_32(dpm);
|
||||
|
||||
/* (void) */ dpm->finish(dpm);
|
||||
done:
|
||||
return retval;
|
||||
@@ -1133,20 +974,15 @@ int arm_dpm_setup(struct arm_dpm *dpm)
|
||||
|
||||
/* register access setup */
|
||||
arm->full_context = arm_dpm_full_context;
|
||||
arm->read_core_reg = arm->read_core_reg ? : arm_dpm_read_core_reg;
|
||||
arm->write_core_reg = arm->write_core_reg ? : arm_dpm_write_core_reg;
|
||||
arm->read_core_reg = arm_dpm_read_core_reg;
|
||||
arm->write_core_reg = arm_dpm_write_core_reg;
|
||||
|
||||
if (arm->core_cache != NULL) {
|
||||
if (arm->core_state == ARM_STATE_AARCH64) {
|
||||
cache = armv8_build_reg_cache(target);
|
||||
target->reg_cache = cache;
|
||||
} else {
|
||||
cache = arm_build_reg_cache(target, arm);
|
||||
*register_get_last_cache_p(&target->reg_cache) = cache;
|
||||
}
|
||||
|
||||
cache = arm_build_reg_cache(target, arm);
|
||||
if (!cache)
|
||||
return ERROR_FAIL;
|
||||
|
||||
*register_get_last_cache_p(&target->reg_cache) = cache;
|
||||
}
|
||||
|
||||
/* coprocessor access setup */
|
||||
@@ -1163,20 +999,10 @@ int arm_dpm_setup(struct arm_dpm *dpm)
|
||||
target->type->add_watchpoint = dpm_add_watchpoint;
|
||||
target->type->remove_watchpoint = dpm_remove_watchpoint;
|
||||
|
||||
|
||||
if (dpm->arm_reg_current == 0)
|
||||
dpm->arm_reg_current = arm_reg_current;
|
||||
|
||||
/* FIXME add vector catch support */
|
||||
|
||||
if (arm->core_state == ARM_STATE_AARCH64) {
|
||||
dpm->nbp = 1 + ((dpm->didr >> 24) & 0xf);
|
||||
dpm->nwp = 1 + ((dpm->didr >> 28) & 0xf);
|
||||
} else {
|
||||
dpm->nbp = 1 + ((dpm->didr >> 12) & 0xf);
|
||||
dpm->nwp = 1 + ((dpm->didr >> 20) & 0xf);
|
||||
}
|
||||
|
||||
dpm->nbp = 1 + ((dpm->didr >> 24) & 0xf);
|
||||
dpm->nwp = 1 + ((dpm->didr >> 28) & 0xf);
|
||||
dpm->dbp = calloc(dpm->nbp, sizeof *dpm->dbp);
|
||||
dpm->dwp = calloc(dpm->nwp, sizeof *dpm->dwp);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user