arm_opcode: Add support for ARM MCRR/MRRC 28/5228/8
authorFlorian Fainelli <f.fainelli@gmail.com>
Mon, 18 Mar 2019 23:00:07 +0000 (16:00 -0700)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 14 Oct 2023 11:55:42 +0000 (11:55 +0000)
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>
src/target/arm.h
src/target/arm_dpm.c
src/target/arm_dpm.h
src/target/arm_opcodes.h
src/target/armv4_5.c
src/target/cortex_a.c

index cc0f14cb648c36af4cd5ef0b83d96d7c4e124822..d5053afb814d2d61fc8114af2f3a9d58e661b56d 100644 (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,
index ab9b50e2302a89fce24aed831dcec48c687a20db..9f3a444afc5dae407e5c8a03783ef666b02e7107 100644 (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) {
index d35e9f68d6dfd61a01dde4275c281d5ac28969cf..2da4631112d465fef4818d81d5343c81a18f6a46 100644 (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);
 
index c182f41c4efafa1363a84abe3113d16355b52832..c8ce51f299d9b570290b609722a37245d2204bbf 100644 (file)
        (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
        (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
  */
index 8e3f22417d3a80407c9604c09c53820ad1567620..7debb94984882b02ab4269efc7fa70293c9d6da1 100644 (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;
 }
index abfd6ac5f140c9252330b4037f7978a3c6031ac8..ba3349d09a98e695ac2ada23855ce492e8329403 100644 (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;

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)