arm_opcode: Add support for ARM MCRR/MRRC
Add support for the ARM MCRR/MRRC instructions which require the use of two registers to transfer a 64-bit co-processor registers. We are going to use this in a subsequent patch in order to properly dump 64-bit page table descriptors that exist on ARMv7A with VMSA extensions. We make use of r0 and r1 to transfer 64-bit quantities to/from DCC. Change-Id: Ic4975026c1ae4f2853795575ac7701d541248736 Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Michael Chalfant <michael.chalfant@gmail.com> Reviewed-on: https://review.openocd.org/c/openocd/+/5228 Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
parent
1bc4182ceb
commit
d27a3a00b8
|
@ -231,12 +231,22 @@ struct arm {
|
|||
uint32_t crn, uint32_t crm,
|
||||
uint32_t *value);
|
||||
|
||||
/** Read coprocessor to two registers. */
|
||||
int (*mrrc)(struct target *target, int cpnum,
|
||||
uint32_t op, uint32_t crm,
|
||||
uint64_t *value);
|
||||
|
||||
/** Write coprocessor register. */
|
||||
int (*mcr)(struct target *target, int cpnum,
|
||||
uint32_t op1, uint32_t op2,
|
||||
uint32_t crn, uint32_t crm,
|
||||
uint32_t value);
|
||||
|
||||
/** Write coprocessor from two registers. */
|
||||
int (*mcrr)(struct target *target, int cpnum,
|
||||
uint32_t op, uint32_t crm,
|
||||
uint64_t value);
|
||||
|
||||
void *arch_info;
|
||||
|
||||
/** For targets conforming to ARM Debug Interface v5,
|
||||
|
|
|
@ -63,6 +63,29 @@ static int dpm_mrc(struct target *target, int cpnum,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int dpm_mrrc(struct target *target, int cpnum,
|
||||
uint32_t op, uint32_t crm, uint64_t *value)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
struct arm_dpm *dpm = arm->dpm;
|
||||
int retval;
|
||||
|
||||
retval = dpm->prepare(dpm);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
LOG_DEBUG("MRRC p%d, %d, r0, r1, c%d", cpnum,
|
||||
(int)op, (int)crm);
|
||||
|
||||
/* read coprocessor register into R0, R1; return via DCC */
|
||||
retval = dpm->instr_read_data_r0_r1(dpm,
|
||||
ARMV5_T_MRRC(cpnum, op, 0, 1, crm),
|
||||
value);
|
||||
|
||||
/* (void) */ dpm->finish(dpm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int dpm_mcr(struct target *target, int cpnum,
|
||||
uint32_t op1, uint32_t op2, uint32_t crn, uint32_t crm,
|
||||
uint32_t value)
|
||||
|
@ -88,6 +111,29 @@ static int dpm_mcr(struct target *target, int cpnum,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int dpm_mcrr(struct target *target, int cpnum,
|
||||
uint32_t op, uint32_t crm, uint64_t value)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
struct arm_dpm *dpm = arm->dpm;
|
||||
int retval;
|
||||
|
||||
retval = dpm->prepare(dpm);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
LOG_DEBUG("MCRR p%d, %d, r0, r1, c%d", cpnum,
|
||||
(int)op, (int)crm);
|
||||
|
||||
/* read DCC into r0, r1; then write coprocessor register from R0, R1 */
|
||||
retval = dpm->instr_write_data_r0_r1(dpm,
|
||||
ARMV5_T_MCRR(cpnum, op, 0, 1, crm), value);
|
||||
|
||||
/* (void) */ dpm->finish(dpm);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
|
@ -1070,6 +1116,8 @@ int arm_dpm_setup(struct arm_dpm *dpm)
|
|||
/* coprocessor access setup */
|
||||
arm->mrc = dpm_mrc;
|
||||
arm->mcr = dpm_mcr;
|
||||
arm->mrrc = dpm_mrrc;
|
||||
arm->mcrr = dpm_mcrr;
|
||||
|
||||
/* breakpoint setup -- optional until it works everywhere */
|
||||
if (!target->type->add_breakpoint) {
|
||||
|
|
|
@ -72,6 +72,12 @@ struct arm_dpm {
|
|||
int (*instr_write_data_r0)(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint32_t data);
|
||||
|
||||
/**
|
||||
* Runs two instructions, writing data to R0 and R1 before execution.
|
||||
*/
|
||||
int (*instr_write_data_r0_r1)(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint64_t data);
|
||||
|
||||
/** Runs one instruction, writing data to R0 before execution. */
|
||||
int (*instr_write_data_r0_64)(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint64_t data);
|
||||
|
@ -92,6 +98,13 @@ struct arm_dpm {
|
|||
int (*instr_read_data_r0)(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint32_t *data);
|
||||
|
||||
/**
|
||||
* Runs two instructions, reading data from r0 and r1 after
|
||||
* execution.
|
||||
*/
|
||||
int (*instr_read_data_r0_r1)(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint64_t *data);
|
||||
|
||||
int (*instr_read_data_r0_64)(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint64_t *data);
|
||||
|
||||
|
|
|
@ -187,6 +187,17 @@
|
|||
(0xee100010 | (crm) | ((op2) << 5) | ((cp) << 8) \
|
||||
| ((rd) << 12) | ((crn) << 16) | ((op1) << 21))
|
||||
|
||||
/* Move to two ARM registers from coprocessor
|
||||
* cp: Coprocessor number
|
||||
* op: Coprocessor opcode
|
||||
* rt: destination register 1
|
||||
* rt2: destination register 2
|
||||
* crm: coprocessor source register
|
||||
*/
|
||||
#define ARMV5_T_MRRC(cp, op, rt, rt2, crm) \
|
||||
(0xec500000 | (crm) | ((op) << 4) | ((cp) << 8) \
|
||||
| ((rt) << 12) | ((rt2) << 16))
|
||||
|
||||
/* Move to coprocessor from ARM register
|
||||
* cp: Coprocessor number
|
||||
* op1: Coprocessor opcode
|
||||
|
@ -199,6 +210,17 @@
|
|||
(0xee000010 | (crm) | ((op2) << 5) | ((cp) << 8) \
|
||||
| ((rd) << 12) | ((crn) << 16) | ((op1) << 21))
|
||||
|
||||
/* Move to coprocessor from two ARM registers
|
||||
* cp: Coprocessor number
|
||||
* op: Coprocessor opcode
|
||||
* rt: destination register 1
|
||||
* rt2: destination register 2
|
||||
* crm: coprocessor source register
|
||||
*/
|
||||
#define ARMV5_T_MCRR(cp, op, rt, rt2, crm) \
|
||||
(0xec400000 | (crm) | ((op) << 4) | ((cp) << 8) \
|
||||
| ((rt) << 12) | ((rt2) << 16))
|
||||
|
||||
/* Breakpoint instruction (ARMv5)
|
||||
* im: 16-bit immediate
|
||||
*/
|
||||
|
|
|
@ -1093,6 +1093,94 @@ COMMAND_HANDLER(handle_armv4_5_mcrmrc)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_armv4_5_mcrrmrrc)
|
||||
{
|
||||
bool is_mcrr = false;
|
||||
unsigned int arg_cnt = 3;
|
||||
|
||||
if (!strcmp(CMD_NAME, "mcrr")) {
|
||||
is_mcrr = true;
|
||||
arg_cnt = 4;
|
||||
}
|
||||
|
||||
if (arg_cnt != CMD_ARGC)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
if (!target) {
|
||||
command_print(CMD, "no current target");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (!target_was_examined(target)) {
|
||||
command_print(CMD, "%s: not yet examined", target_name(target));
|
||||
return ERROR_TARGET_NOT_EXAMINED;
|
||||
}
|
||||
|
||||
struct arm *arm = target_to_arm(target);
|
||||
if (!is_arm(arm)) {
|
||||
command_print(CMD, "%s: not an ARM", target_name(target));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (target->state != TARGET_HALTED)
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
|
||||
int cpnum;
|
||||
uint32_t op1;
|
||||
uint32_t crm;
|
||||
uint64_t value;
|
||||
|
||||
/* NOTE: parameter sequence matches ARM instruction set usage:
|
||||
* MCRR pNUM, op1, rX1, rX2, CRm ; write CP from rX1 and rX2
|
||||
* MREC pNUM, op1, rX1, rX2, CRm ; read CP into rX1 and rX2
|
||||
* The "rXn" are necessarily omitted; they use Tcl mechanisms.
|
||||
*/
|
||||
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], cpnum);
|
||||
if (cpnum & ~0xf) {
|
||||
command_print(CMD, "coprocessor %d out of range", cpnum);
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], op1);
|
||||
if (op1 & ~0xf) {
|
||||
command_print(CMD, "op1 %d out of range", op1);
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], crm);
|
||||
if (crm & ~0xf) {
|
||||
command_print(CMD, "CRm %d out of range", crm);
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME change the call syntax here ... simplest to just pass
|
||||
* the MRC() or MCR() instruction to be executed. That will also
|
||||
* let us support the "mrrc2" and "mcrr2" opcodes (toggling one bit)
|
||||
* if that's ever needed.
|
||||
*/
|
||||
if (is_mcrr) {
|
||||
COMMAND_PARSE_NUMBER(u64, CMD_ARGV[3], value);
|
||||
|
||||
/* NOTE: parameters reordered! */
|
||||
/* ARMV5_T_MCRR(cpnum, op1, crm) */
|
||||
int retval = arm->mcrr(target, cpnum, op1, crm, value);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
} else {
|
||||
value = 0;
|
||||
/* NOTE: parameters reordered! */
|
||||
/* ARMV5_T_MRRC(cpnum, op1, crm) */
|
||||
int retval = arm->mrrc(target, cpnum, op1, crm, &value);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
command_print(CMD, "0x%" PRIx64, value);
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static const struct command_registration arm_exec_command_handlers[] = {
|
||||
{
|
||||
.name = "reg",
|
||||
|
@ -1115,6 +1203,20 @@ static const struct command_registration arm_exec_command_handlers[] = {
|
|||
.help = "read coprocessor register",
|
||||
.usage = "cpnum op1 CRn CRm op2",
|
||||
},
|
||||
{
|
||||
.name = "mcrr",
|
||||
.mode = COMMAND_EXEC,
|
||||
.handler = handle_armv4_5_mcrrmrrc,
|
||||
.help = "write coprocessor 64-bit register",
|
||||
.usage = "cpnum op1 CRm value",
|
||||
},
|
||||
{
|
||||
.name = "mrrc",
|
||||
.mode = COMMAND_EXEC,
|
||||
.handler = handle_armv4_5_mcrrmrrc,
|
||||
.help = "read coprocessor 64-bit register",
|
||||
.usage = "cpnum op1 CRm",
|
||||
},
|
||||
{
|
||||
.chain = arm_all_profiles_command_handlers,
|
||||
},
|
||||
|
@ -1669,6 +1771,14 @@ static int arm_default_mrc(struct target *target, int cpnum,
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static int arm_default_mrrc(struct target *target, int cpnum,
|
||||
uint32_t op, uint32_t crm,
|
||||
uint64_t *value)
|
||||
{
|
||||
LOG_ERROR("%s doesn't implement MRRC", target_type_name(target));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static int arm_default_mcr(struct target *target, int cpnum,
|
||||
uint32_t op1, uint32_t op2,
|
||||
uint32_t crn, uint32_t crm,
|
||||
|
@ -1678,6 +1788,14 @@ static int arm_default_mcr(struct target *target, int cpnum,
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static int arm_default_mcrr(struct target *target, int cpnum,
|
||||
uint32_t op, uint32_t crm,
|
||||
uint64_t value)
|
||||
{
|
||||
LOG_ERROR("%s doesn't implement MCRR", target_type_name(target));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int arm_init_arch_info(struct target *target, struct arm *arm)
|
||||
{
|
||||
target->arch_info = arm;
|
||||
|
@ -1697,8 +1815,12 @@ int arm_init_arch_info(struct target *target, struct arm *arm)
|
|||
|
||||
if (!arm->mrc)
|
||||
arm->mrc = arm_default_mrc;
|
||||
if (!arm->mrrc)
|
||||
arm->mrrc = arm_default_mrrc;
|
||||
if (!arm->mcr)
|
||||
arm->mcr = arm_default_mcr;
|
||||
if (!arm->mcrr)
|
||||
arm->mcrr = arm_default_mcrr;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
|
|
@ -471,6 +471,28 @@ static int cortex_a_instr_write_data_r0(struct arm_dpm *dpm,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int cortex_a_instr_write_data_r0_r1(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint64_t data)
|
||||
{
|
||||
struct cortex_a_common *a = dpm_to_a(dpm);
|
||||
uint32_t dscr = DSCR_INSTR_COMP;
|
||||
int retval;
|
||||
|
||||
retval = cortex_a_instr_write_data_rt_dcc(dpm, 0, data & 0xffffffffULL);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cortex_a_instr_write_data_rt_dcc(dpm, 1, data >> 32);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* then the opcode, taking data from R0, R1 */
|
||||
retval = cortex_a_exec_opcode(a->armv7a_common.arm.target,
|
||||
opcode,
|
||||
&dscr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cortex_a_instr_cpsr_sync(struct arm_dpm *dpm)
|
||||
{
|
||||
struct target *target = dpm->arm->target;
|
||||
|
@ -539,6 +561,29 @@ static int cortex_a_instr_read_data_r0(struct arm_dpm *dpm,
|
|||
return cortex_a_instr_read_data_rt_dcc(dpm, 0, data);
|
||||
}
|
||||
|
||||
static int cortex_a_instr_read_data_r0_r1(struct arm_dpm *dpm,
|
||||
uint32_t opcode, uint64_t *data)
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
int retval;
|
||||
|
||||
/* the opcode, writing data to RO, R1 */
|
||||
retval = cortex_a_instr_read_data_r0(dpm, opcode, &lo);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
*data = lo;
|
||||
|
||||
/* write R1 to DCC */
|
||||
retval = cortex_a_instr_read_data_rt_dcc(dpm, 1, &hi);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
*data |= (uint64_t)hi << 32;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cortex_a_bpwp_enable(struct arm_dpm *dpm, unsigned index_t,
|
||||
uint32_t addr, uint32_t control)
|
||||
{
|
||||
|
@ -612,10 +657,12 @@ static int cortex_a_dpm_setup(struct cortex_a_common *a, uint32_t didr)
|
|||
|
||||
dpm->instr_write_data_dcc = cortex_a_instr_write_data_dcc;
|
||||
dpm->instr_write_data_r0 = cortex_a_instr_write_data_r0;
|
||||
dpm->instr_write_data_r0_r1 = cortex_a_instr_write_data_r0_r1;
|
||||
dpm->instr_cpsr_sync = cortex_a_instr_cpsr_sync;
|
||||
|
||||
dpm->instr_read_data_dcc = cortex_a_instr_read_data_dcc;
|
||||
dpm->instr_read_data_r0 = cortex_a_instr_read_data_r0;
|
||||
dpm->instr_read_data_r0_r1 = cortex_a_instr_read_data_r0_r1;
|
||||
|
||||
dpm->bpwp_enable = cortex_a_bpwp_enable;
|
||||
dpm->bpwp_disable = cortex_a_bpwp_disable;
|
||||
|
|
Loading…
Reference in New Issue