Skip to content

Commit

Permalink
arm_opcode: Add support for ARM MCRR/MRRC
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Michael Chalfant <[email protected]>
Reviewed-on: https://review.openocd.org/c/openocd/+/5228
Tested-by: jenkins
Reviewed-by: Antonio Borneo <[email protected]>
  • Loading branch information
ffainelli authored and borneoa committed Oct 14, 2023
1 parent 1bc4182 commit d27a3a0
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/target/arm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
48 changes: 48 additions & 0 deletions src/target/arm_dpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}

/*----------------------------------------------------------------------*/

/*
Expand Down Expand Up @@ -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) {
Expand Down
13 changes: 13 additions & 0 deletions src/target/arm_dpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand Down
22 changes: 22 additions & 0 deletions src/target/arm_opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
*/
Expand Down
122 changes: 122 additions & 0 deletions src/target/armv4_5.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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,
},
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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;
}
47 changes: 47 additions & 0 deletions src/target/cortex_a.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit d27a3a0

Please sign in to comment.