target/mips32: update coprocessor 0 command 05/7905/21
authorWalter Ji <walter.ji@oss.cipunited.com>
Fri, 17 Nov 2023 07:13:21 +0000 (15:13 +0800)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 6 Jan 2024 13:51:26 +0000 (13:51 +0000)
Update mips32 cp0 command, it accepts cp0 reg names now.
Updated mips32 cp0 description.

Change-Id: Ib23dd13519def77a657c9c5bb039276746207b9b
Signed-off-by: Walter Ji <walter.ji@oss.cipunited.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/7905
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
Tested-by: jenkins
doc/openocd.texi
src/target/mips32.c
src/target/mips32.h

index cf41bc538aac1381120a07bfeb676e631494298f..a2af45114f60967023c1e3d1d9d8ccfde1d0ab0e 100644 (file)
@@ -10998,9 +10998,10 @@ The term ASE means Application-Specific Extension, ASEs provide features that
 improve the efficiency and performance of certain workloads, such as
 digital signal processing(DSP), Virtualization(VZ), Multi-Threading(MT),
 SIMD(MSA) and more.
-The MIPS CPU Uses Coprocessors to configure its behaviour or to let software
-know the capabilities of current CPU, the commonly used ones are Config0~3 Registers
-and Status register.
+
+MIPS Cores use Coprocessors(CPx) to configure their behaviour or to let software
+know the capabilities of current CPU, the main Coprocessor is CP0, containing 32
+registers with a maximum select number of 7.
 
 @subsection MIPS FPU & Vector Registers
 
@@ -11028,8 +11029,9 @@ Display or set scan delay in nano seconds. A value below 2_000_000 will set the
 scan delay into legacy mode.
 @end deffn
 
-@deffn {Config Command} {mips32 cp0} regnum select [value]
-Displays or sets coprocessor 0 register by register number and select.
+@deffn {Config Command} {mips32 cp0} [[reg_name|regnum select] [value]]
+Displays or sets coprocessor 0 register by register number and select or their name.
+This command shows all available cp0 register if no arguments are provided.
 
 For common MIPS Coprocessor 0 registers, you can find the definitions of them
 on MIPS Privileged Resource Architecture Documents(MIPS Document MD00090).
index af5f2c50d80701d3a22c6b4dd1333e1c797ff280..9cebc48f616c89e2a2f97c0775bc0941c73cbdad 100644 (file)
@@ -834,6 +834,27 @@ int mips32_cpu_probe(struct target *target)
                        break;
                }
                break;
+
+       /* Determine which CP0 registers are available in the current processor core */
+       case PRID_COMP_MTI:
+               switch (entry->prid & PRID_IMP_MASK) {
+               case PRID_IMP_MAPTIV_UC:
+                       mips32->cp0_mask = MIPS_CP0_MAPTIV_UC;
+                       break;
+               case PRID_IMP_MAPTIV_UP:
+               case PRID_IMP_M5150:
+                       mips32->cp0_mask = MIPS_CP0_MAPTIV_UP;
+                       break;
+               case PRID_IMP_IAPTIV:
+               case PRID_IMP_IAPTIV_CM:
+                       mips32->cp0_mask = MIPS_CP0_IAPTIV;
+                       break;
+               default:
+                       /* CP0 mask should be the same as MK4 by default */
+                       mips32->cp0_mask = MIPS_CP0_MK4;
+                       break;
+               }
+
        default:
                break;
        }
@@ -1212,12 +1233,239 @@ static int mips32_read_config_mmu(struct mips_ejtag *ejtag_info)
 }
 
 /**
- * MIPS32 targets expose command interface
- * to manipulate CP0 registers
+ * mips32_cp0_find_register_by_name - Find CP0 register by its name.
+ * @param[in] cp0_mask: Mask to filter out irrelevant registers.
+ * @param[in] reg_name: Name of the register to find.
+ *
+ * @brief This function iterates through mips32_cp0_regs to find a register
+ * matching reg_name, considering cp0_mask to filter out registers
+ * not relevant for the current core.
+ *
+ * @return Pointer to the found register, or NULL if not found.
+ */
+static const struct mips32_cp0 *mips32_cp0_find_register_by_name(uint32_t cp0_mask, const char *reg_name)
+{
+       if (reg_name)
+               for (unsigned int i = 0; i < MIPS32NUMCP0REGS; i++) {
+                       if ((mips32_cp0_regs[i].core & cp0_mask) == 0)
+                               continue;
+
+                       if (strcmp(mips32_cp0_regs[i].name, reg_name) == 0)
+                               return &mips32_cp0_regs[i];
+               }
+       return NULL;
+}
+
+/**
+ * mips32_cp0_get_all_regs - Print all CP0 registers and their values.
+ * @param[in] cmd: Command invocation context.
+ * @param[in] ejtag_info: EJTAG interface information.
+ * @param[in] cp0_mask: Mask to identify relevant registers.
+ *
+ * @brief Iterates over all CP0 registers, reads their values, and prints them.
+ * Only considers registers relevant to the current core, as defined by cp0_mask.
+ *
+ * @return ERROR_OK on success; error code on failure.
+ */
+static int mips32_cp0_get_all_regs(struct command_invocation *cmd, struct mips_ejtag *ejtag_info, uint32_t cp0_mask)
+{
+       uint32_t value;
+
+       for (unsigned int i = 0; i < MIPS32NUMCP0REGS; i++) {
+               /* Register name not valid for this core */
+               if ((mips32_cp0_regs[i].core & cp0_mask) == 0)
+                       continue;
+
+               int retval = mips32_cp0_read(ejtag_info, &value, mips32_cp0_regs[i].reg, mips32_cp0_regs[i].sel);
+               if (retval != ERROR_OK) {
+                       command_print(CMD, "Error: couldn't access reg %s", mips32_cp0_regs[i].name);
+                       return retval;
+               }
+
+               command_print(CMD, "%*s: 0x%8.8" PRIx32, 14, mips32_cp0_regs[i].name, value);
+       }
+       return ERROR_OK;
+}
+
+/**
+ * mips32_cp0_get_reg_by_name - Read and print a CP0 register's value by name.
+ * @param[in] cmd: Command invocation context.
+ * @param[in] ejtag_info: EJTAG interface information.
+ * @param[in] cp0_mask: Mask to identify relevant registers.
+ *
+ * @brief Finds a CP0 register by name, reads its value, and prints it.
+ * Handles error scenarios like register not found or read failure.
+ *
+ * @return ERROR_OK on success; error code on failure.
+ */
+static int mips32_cp0_get_reg_by_name(struct command_invocation *cmd, struct mips_ejtag *ejtag_info, uint32_t cp0_mask)
+{
+       const struct mips32_cp0 *cp0_regs = mips32_cp0_find_register_by_name(cp0_mask, CMD_ARGV[0]);
+       if (!cp0_regs) {
+               command_print(CMD, "Error: Register '%s' not found", CMD_ARGV[0]);
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       uint32_t value;
+       int retval = mips32_cp0_read(ejtag_info, &value, cp0_regs->reg, cp0_regs->sel);
+       if (retval != ERROR_OK) {
+               command_print(CMD, "Error: Encounter an Error while reading cp0 reg %d sel %d",
+                                       cp0_regs->reg, cp0_regs->sel);
+               return retval;
+       }
+
+       command_print(CMD, "0x%8.8" PRIx32, value);
+       return ERROR_OK;
+}
+
+/**
+ * mips32_cp0_get_reg_by_number - Read and print a CP0 register's value by number.
+ * @param[in] cmd: Command invocation context.
+ * @param[in] ejtag_info: EJTAG interface information.
+ *
+ * @brief Reads a specific CP0 register (identified by number and selection) and prints its value.
+ * The register number and selection are parsed from the command arguments.
+ *
+ * @return ERROR_OK on success; error code on failure.
+ */
+static int mips32_cp0_get_reg_by_number(struct command_invocation *cmd, struct mips_ejtag *ejtag_info)
+{
+       uint32_t cp0_reg, cp0_sel, value;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], cp0_reg);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], cp0_sel);
+
+       int retval = mips32_cp0_read(ejtag_info, &value, cp0_reg, cp0_sel);
+       if (retval != ERROR_OK) {
+               command_print(CMD,
+                               "Error: couldn't access reg %" PRIu32,
+                               cp0_reg);
+               return retval;
+       }
+
+       command_print(CMD, "cp0 reg %" PRIu32 ", select %" PRIu32 ": %8.8" PRIx32,
+                       cp0_reg, cp0_sel, value);
+       return ERROR_OK;
+}
+
+/**
+ * mips32_cp0_set_reg_by_name - Write to a CP0 register identified by name.
+ * @param[in] cmd: Command invocation context.
+ * @param[in] mips32: Common MIPS32 data structure.
+ * @param[in] ejtag_info: EJTAG interface information.
+ *
+ * @brief Writes a value to a CP0 register specified by name. Updates internal
+ * cache if specific registers (STATUS, CAUSE, DEPC, GUESTCTL1) are modified.
+ *
+ * @return ERROR_OK on success; error code on failure.
+ */
+static int mips32_cp0_set_reg_by_name(struct command_invocation *cmd,
+               struct mips32_common *mips32, struct mips_ejtag *ejtag_info)
+{
+       const struct mips32_cp0 *cp0_regs = mips32_cp0_find_register_by_name(mips32->cp0_mask, CMD_ARGV[0]);
+       if (!cp0_regs) {
+               command_print(CMD, "Error: Register '%s' not found", CMD_ARGV[0]);
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+
+       uint32_t value;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+
+       if (cp0_regs->reg == MIPS32_C0_STATUS && cp0_regs->sel == 0) {
+               /* Update cached Status register if user is writing to Status */
+               mips32->core_regs.cp0[MIPS32_REG_C0_STATUS_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_STATUS_INDEX].dirty = 1;
+       } else if (cp0_regs->reg == MIPS32_C0_CAUSE && cp0_regs->sel == 0) {
+               /* Update register cache with new value if its Cause */
+               mips32->core_regs.cp0[MIPS32_REG_C0_CAUSE_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_CAUSE_INDEX].dirty = 1;
+       } else if (cp0_regs->reg == MIPS32_C0_DEPC && cp0_regs->sel == 0) {
+               /* Update cached PC if its DEPC */
+               mips32->core_regs.cp0[MIPS32_REG_C0_PC_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_PC_INDEX].dirty = 1;
+       } else if (cp0_regs->reg == MIPS32_C0_GUESTCTL1 && cp0_regs->sel == 4) {
+               /* Update cached guestCtl1 */
+               mips32->core_regs.cp0[MIPS32_REG_C0_GUESTCTL1_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_GUESTCTL1_INDEX].dirty = 1;
+       }
+
+       int retval = mips32_cp0_write(ejtag_info, value,
+                                                               cp0_regs->reg,
+                                                               cp0_regs->sel);
+       if (retval != ERROR_OK) {
+               command_print(CMD, "Error: Encounter an Error while writing to cp0 reg %d, sel %d",
+                                       cp0_regs->reg, cp0_regs->sel);
+               return retval;
+       }
+
+       command_print(CMD, "cp0 reg %s (%u, select %u: %8.8" PRIx32 ")",
+                       CMD_ARGV[0], cp0_regs->reg, cp0_regs->sel, value);
+       return ERROR_OK;
+}
+
+/**
+ * mips32_cp0_set_reg_by_number - Write to a CP0 register identified by number.
+ * @param[in] cmd: Command invocation context.
+ * @param[in] mips32: Common MIPS32 data structure.
+ * @param[in] ejtag_info: EJTAG interface information.
+ *
+ * @brief Writes a value to a CP0 register specified by number and selection.
+ * Handles special cases like updating the internal cache for certain registers.
+ *
+ * @return ERROR_OK on success; error code on failure.
+ */
+static int mips32_cp0_set_reg_by_number(struct command_invocation *cmd,
+               struct mips32_common *mips32, struct mips_ejtag *ejtag_info)
+{
+       uint32_t cp0_reg, cp0_sel, value;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], cp0_reg);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], cp0_sel);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value);
+
+       if (cp0_reg == MIPS32_C0_STATUS && cp0_sel == 0) {
+               /* Update cached status register if user is writing to Status register */
+               mips32->core_regs.cp0[MIPS32_REG_C0_STATUS_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_STATUS_INDEX].dirty = 1;
+       } else if (cp0_reg == MIPS32_C0_CAUSE && cp0_sel == 0) {
+               /* Update register cache with new value if its Cause register */
+               mips32->core_regs.cp0[MIPS32_REG_C0_CAUSE_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_CAUSE_INDEX].dirty = 1;
+       } else if (cp0_reg == MIPS32_C0_DEPC && cp0_sel == 0) {
+               /* Update cached PC if its DEPC */
+               mips32->core_regs.cp0[MIPS32_REG_C0_PC_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_PC_INDEX].dirty = 1;
+       } else if (cp0_reg == MIPS32_C0_GUESTCTL1 && cp0_sel == 4) {
+               /* Update cached guestCtl1, too */
+               mips32->core_regs.cp0[MIPS32_REG_C0_GUESTCTL1_INDEX] = value;
+               mips32->core_cache->reg_list[MIPS32_REGLIST_C0_GUESTCTL1_INDEX].dirty = 1;
+       }
+
+       int retval = mips32_cp0_write(ejtag_info, value, cp0_reg, cp0_sel);
+       if (retval != ERROR_OK) {
+               command_print(CMD,
+                               "Error: couldn't access cp0 reg %" PRIu32 ", select %" PRIu32,
+                               cp0_reg,  cp0_sel);
+               return retval;
+       }
+
+       command_print(CMD, "cp0 reg %" PRIu32 ", select %" PRIu32 ": %8.8" PRIx32,
+                       cp0_reg, cp0_sel, value);
+       return ERROR_OK;
+}
+
+/**
+ * mips32_handle_cp0_command - Handle commands related to CP0 registers.
+ * @cmd: Command invocation context.
+ *
+ * Orchestrates different operations on CP0 registers based on the command arguments.
+ * Supports operations like reading all registers, reading/writing a specific register
+ * by name or number.
+ *
+ * Return: ERROR_OK on success; error code on failure.
  */
 COMMAND_HANDLER(mips32_handle_cp0_command)
 {
-       int retval;
+       int retval, tmp;
        struct target *target = get_current_target(CMD_CTX);
        struct mips32_common *mips32 = target_to_mips32(target);
        struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
@@ -1232,43 +1480,30 @@ COMMAND_HANDLER(mips32_handle_cp0_command)
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       /* two or more argument, access a single register/select (write if third argument is given) */
-       if (CMD_ARGC < 2)
-               return ERROR_COMMAND_SYNTAX_ERROR;
-       else {
-               uint32_t cp0_reg, cp0_sel;
-               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], cp0_reg);
-               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], cp0_sel);
-
-               if (CMD_ARGC == 2) {
-                       uint32_t value;
-
-                       retval = mips32_cp0_read(ejtag_info, &value, cp0_reg, cp0_sel);
-                       if (retval != ERROR_OK) {
-                               command_print(CMD,
-                                               "couldn't access reg %" PRIu32,
-                                               cp0_reg);
-                               return ERROR_OK;
-                       }
-                       command_print(CMD, "cp0 reg %" PRIu32 ", select %" PRIu32 ": %8.8" PRIx32,
-                                       cp0_reg, cp0_sel, value);
+       switch (CMD_ARGC) {
+               case 0: /* No arg => print out all cp0 regs */
+                       retval = mips32_cp0_get_all_regs(CMD, ejtag_info, mips32->cp0_mask);
+                       break;
+               case 1: /* 1 arg => get cp0 #reg/#sel value by name */
+                       retval = mips32_cp0_get_reg_by_name(CMD, ejtag_info, mips32->cp0_mask);
+                       break;
+               case 2: /* 2 args => get cp0 reg/sel value or set value by name */
+                       tmp = *CMD_ARGV[0];
+                       if (isdigit(tmp)) /* starts from number then args are #reg and #sel */
+                               retval = mips32_cp0_get_reg_by_number(CMD, ejtag_info);
+                       else /* or set value by register name */
+                               retval = mips32_cp0_set_reg_by_name(CMD, mips32, ejtag_info);
 
-               } else if (CMD_ARGC == 3) {
-                       uint32_t value;
-                       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value);
-                       retval = mips32_cp0_write(ejtag_info, value, cp0_reg, cp0_sel);
-                       if (retval != ERROR_OK) {
-                               command_print(CMD,
-                                               "couldn't access cp0 reg %" PRIu32 ", select %" PRIu32,
-                                               cp0_reg,  cp0_sel);
-                               return ERROR_OK;
-                       }
-                       command_print(CMD, "cp0 reg %" PRIu32 ", select %" PRIu32 ": %8.8" PRIx32,
-                                       cp0_reg, cp0_sel, value);
-               }
+                       break;
+               case 3: /* 3 args => set cp0 reg/sel value*/
+                       retval = mips32_cp0_set_reg_by_number(CMD, mips32, ejtag_info);
+                       break;
+               default: /* Other argc => err */
+                       retval = ERROR_COMMAND_SYNTAX_ERROR;
+                       break;
        }
 
-       return ERROR_OK;
+       return retval;
 }
 
 /**
@@ -1500,7 +1735,7 @@ static const struct command_registration mips32_exec_command_handlers[] = {
                .name = "cp0",
                .handler = mips32_handle_cp0_command,
                .mode = COMMAND_EXEC,
-               .usage = "regnum select [value]",
+               .usage = "[[reg_name|regnum select] [value]]",
                .help = "display/modify cp0 register",
        },
        {
index 1bb75da9950b1ffc0d3626aff676b6045c063a17..46c2ad933c670b86149099416acbc042565ba35d 100644 (file)
@@ -84,7 +84,7 @@
 /* CP1 FIR register fields */
 #define MIPS32_CP1_FIR_F64_SHIFT       22
 
-static const struct {
+static const struct mips32_cp0 {
        unsigned int reg;
        unsigned int sel;
        const char *name;
@@ -202,7 +202,7 @@ static const struct {
        {31, 3, "kscratch2", MIPS_CP0_MAPTIV_UC | MIPS_CP0_MAPTIV_UP},
 };
 
-#define MIPS32NUMCP0REGS ((int)ARRAY_SIZE(mips32_cp0_regs))
+#define MIPS32NUMCP0REGS (ARRAY_SIZE(mips32_cp0_regs))
 
 /* Insert extra NOPs after the DRET instruction on exit from debug. */
 #define        EJTAG_QUIRK_PAD_DRET            BIT(0)
@@ -397,6 +397,12 @@ struct mips32_common {
        int fdc;
        int semihosting;
 
+       /* The cp0 registers implemented on different processor cores could be different, too.
+        * Here you can see most of the registers are implemented on interAptiv, which is
+        * a 2c4t SMP processor, it has more features than M-class processors, like vpe
+        * and other config registers for multhreading. */
+       uint32_t cp0_mask;
+
        /* FPU enabled (cp0.status.cu1) */
        bool fpu_enabled;
        /* FPU mode (cp0.status.fr) */

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)