From b2172ed7d785ff1cd816d93cbed30afb45f1402b Mon Sep 17 00:00:00 2001 From: Walter Ji Date: Fri, 17 Nov 2023 15:13:21 +0800 Subject: [PATCH] target/mips32: update coprocessor 0 command Update mips32 cp0 command, it accepts cp0 reg names now. Updated mips32 cp0 description. Change-Id: Ib23dd13519def77a657c9c5bb039276746207b9b Signed-off-by: Walter Ji Reviewed-on: https://review.openocd.org/c/openocd/+/7905 Reviewed-by: Antonio Borneo Reviewed-by: Oleksij Rempel Tested-by: jenkins --- doc/openocd.texi | 12 +- src/target/mips32.c | 311 ++++++++++++++++++++++++++++++++++++++------ src/target/mips32.h | 10 +- 3 files changed, 288 insertions(+), 45 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index cf41bc538a..a2af45114f 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -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). diff --git a/src/target/mips32.c b/src/target/mips32.c index af5f2c50d8..9cebc48f61 100644 --- a/src/target/mips32.c +++ b/src/target/mips32.c @@ -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", }, { diff --git a/src/target/mips32.h b/src/target/mips32.h index 1bb75da995..46c2ad933c 100644 --- a/src/target/mips32.h +++ b/src/target/mips32.h @@ -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) */