From 3b38226370b21ef1d644cbe303a85c36a58ef365 Mon Sep 17 00:00:00 2001 From: Artur Rojek Date: Sun, 13 Aug 2023 16:47:28 +0200 Subject: [PATCH] mips32: add per-cpu quirks feature Introduce the ability to detect CPUs based on CP0 PRId register and apply cpu specific quirks, which alter the default ejtag behavior. First of those is EJTAG_QUIRK_PAD_DRET, which makes sure extra NOPs are placed after the DRET instruction on exit from debug mode. This fixes resume behavior on Ingenic JZ4780 SoC. The proper detection of some (currently unsupported) CPUs becomes quite complicated, so please consult the following Linux kernel code when adding new CPUs: * arch/mips/include/asm/cpu.h * arch/mips/kernel/cpu-probe.c Change-Id: I0f413d5096cd43ef346b02cea85024985b7face6 Signed-off-by: Artur Rojek Signed-off-by: Paul Fertser Signed-off-by: Oleksij Rempel Reviewed-on: https://review.openocd.org/c/openocd/+/7859 Tested-by: jenkins --- src/target/Makefile.am | 1 + src/target/mips32.c | 58 +++++++++++++++++++++++++++++++++++++++++ src/target/mips32.h | 12 +++++++++ src/target/mips_cpu.h | 27 +++++++++++++++++++ src/target/mips_ejtag.c | 5 +++- src/target/mips_m4k.c | 2 ++ 6 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/target/mips_cpu.h diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 2084de65e6..1fc7d2afa6 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -204,6 +204,7 @@ ARC_SRC = \ %D%/image.h \ %D%/mips32.h \ %D%/mips64.h \ + %D%/mips_cpu.h \ %D%/mips_m4k.h \ %D%/mips_mips64.h \ %D%/mips_ejtag.h \ diff --git a/src/target/mips32.c b/src/target/mips32.c index ce16a7b5d6..4e6d25118e 100644 --- a/src/target/mips32.c +++ b/src/target/mips32.c @@ -18,6 +18,7 @@ #endif #include "mips32.h" +#include "mips_cpu.h" #include "breakpoints.h" #include "algorithm.h" #include "register.h" @@ -693,6 +694,63 @@ int mips32_enable_interrupts(struct target *target, int enable) return ERROR_OK; } +/* read processor identification cp0 register */ +static int mips32_read_c0_prid(struct target *target) +{ + struct mips32_common *mips32 = target_to_mips32(target); + struct mips_ejtag *ejtag_info = &mips32->ejtag_info; + int retval; + + retval = mips32_cp0_read(ejtag_info, &mips32->prid, 15, 0); + if (retval != ERROR_OK) { + LOG_ERROR("processor id not available, failed to read cp0 PRId register"); + mips32->prid = 0; + } + + return retval; +} + +/* + * Detect processor type and apply required quirks. + * + * NOTE: The proper detection of certain CPUs can become quite complicated. + * Please consult the following Linux kernel code when adding new CPUs: + * arch/mips/include/asm/cpu.h + * arch/mips/kernel/cpu-probe.c + */ +int mips32_cpu_probe(struct target *target) +{ + struct mips32_common *mips32 = target_to_mips32(target); + const char *cpu_name = "unknown"; + int retval; + + if (mips32->prid) + return ERROR_OK; /* Already probed once, return early. */ + + retval = mips32_read_c0_prid(target); + if (retval != ERROR_OK) + return retval; + + switch (mips32->prid & PRID_COMP_MASK) { + case PRID_COMP_INGENIC_E1: + switch (mips32->prid & PRID_IMP_MASK) { + case PRID_IMP_XBURST_REV1: + cpu_name = "Ingenic XBurst rev1"; + mips32->cpu_quirks |= EJTAG_QUIRK_PAD_DRET; + break; + default: + break; + } + break; + default: + break; + } + + LOG_DEBUG("CPU: %s (PRId %08x)", cpu_name, mips32->prid); + + return ERROR_OK; +} + /* read config to config3 cp0 registers and log isa implementation */ int mips32_read_config_regs(struct target *target) { diff --git a/src/target/mips32.h b/src/target/mips32.h index 81b6d649d8..3d03e98c58 100644 --- a/src/target/mips32.h +++ b/src/target/mips32.h @@ -13,6 +13,8 @@ #ifndef OPENOCD_TARGET_MIPS32_H #define OPENOCD_TARGET_MIPS32_H +#include + #include "target.h" #include "mips32_pracc.h" @@ -55,6 +57,9 @@ #define MIPS32_SCAN_DELAY_LEGACY_MODE 2000000 +/* Insert extra NOPs after the DRET instruction on exit from debug. */ +#define EJTAG_QUIRK_PAD_DRET BIT(0) + /* offsets into mips32 core register cache */ enum { MIPS32_PC = 37, @@ -91,6 +96,11 @@ struct mips32_common { enum mips32_isa_mode isa_mode; enum mips32_isa_imp isa_imp; + /* processor identification register */ + uint32_t prid; + /* CPU specific quirks */ + uint32_t cpu_quirks; + /* working area for fastdata access */ struct working_area *fast_data_area; @@ -408,6 +418,8 @@ int mips32_enable_interrupts(struct target *target, int enable); int mips32_examine(struct target *target); +int mips32_cpu_probe(struct target *target); + int mips32_read_config_regs(struct target *target); int mips32_register_commands(struct command_context *cmd_ctx); diff --git a/src/target/mips_cpu.h b/src/target/mips_cpu.h new file mode 100644 index 0000000000..8190b32e43 --- /dev/null +++ b/src/target/mips_cpu.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef OPENOCD_TARGET_MIPS_CPU_H +#define OPENOCD_TARGET_MIPS_CPU_H + +/* + * NOTE: The proper detection of certain CPUs can become quite complicated. + * Please consult the following Linux kernel code when adding new CPUs: + * arch/mips/include/asm/cpu.h + * arch/mips/kernel/cpu-probe.c + */ + +/* Assigned Company values for bits 23:16 of the PRId register. */ +#define PRID_COMP_MASK 0xff0000 + +#define PRID_COMP_LEGACY 0x000000 +#define PRID_COMP_INGENIC_E1 0xe10000 + +/* + * Assigned Processor ID (implementation) values for bits 15:8 of the PRId + * register. In order to detect a certain CPU type exactly eventually additional + * registers may need to be examined. + */ +#define PRID_IMP_MASK 0xff00 + +#define PRID_IMP_XBURST_REV1 0x0200 /* XBurst®1 with MXU1.0/MXU1.1 SIMD ISA */ + +#endif /* OPENOCD_TARGET_MIPS_CPU_H */ diff --git a/src/target/mips_ejtag.c b/src/target/mips_ejtag.c index a1a179216d..5c92bf9ee3 100644 --- a/src/target/mips_ejtag.c +++ b/src/target/mips_ejtag.c @@ -259,9 +259,12 @@ int mips_ejtag_exit_debug(struct mips_ejtag *ejtag_info) { struct pa_list pracc_list = {.instr = MIPS32_DRET(ejtag_info->isa), .addr = 0}; struct pracc_queue_info ctx = {.max_code = 1, .pracc_list = &pracc_list, .code_count = 1, .store_count = 0}; + struct mips32_common *mips32 = container_of(ejtag_info, + struct mips32_common, ejtag_info); /* execute our dret instruction */ - ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL, 0); /* shift out instr, omit last check */ + ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL, + mips32->cpu_quirks & EJTAG_QUIRK_PAD_DRET); /* pic32mx workaround, false pending at low core clock */ jtag_add_sleep(1000); diff --git a/src/target/mips_m4k.c b/src/target/mips_m4k.c index 14e3f3b27a..46d241cb30 100644 --- a/src/target/mips_m4k.c +++ b/src/target/mips_m4k.c @@ -100,6 +100,8 @@ static int mips_m4k_debug_entry(struct target *target) /* attempt to find halt reason */ mips_m4k_examine_debug_reason(target); + mips32_cpu_probe(target); + mips32_read_config_regs(target); /* default to mips32 isa, it will be changed below if required */ -- 2.30.2