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:
Florian Fainelli 2019-03-18 16:00:07 -07:00 committed by Antonio Borneo
parent 1bc4182ceb
commit d27a3a00b8
6 changed files with 262 additions and 0 deletions

View File

@ -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,

View File

@ -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) {

View File

@ -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);

View File

@ -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
*/

View File

@ -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;
}

View File

@ -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;