From 380b9b2cd21b2f33c696169dc4c629dc18c8c514 Mon Sep 17 00:00:00 2001 From: Marek Vrbka Date: Fri, 12 Jan 2024 14:38:56 +0100 Subject: [PATCH] target: Fix force-reading of registers and add flush capability 1) OpenOCD has the capability to 'force' a register read from the target. This functionality however silently breaks the register cache: During 'get_reg force' or 'reg force', reg->type->get() is called which will silently overwrite dirty items in the register cache, causing a loss of unwritten register values. This patch fixes that by adding a flush callback for registers, and by using it when it is needed. 2) The register write commands did not have the 'force' flag; this was present for register read commands only. This patch adds it. 3) This patch also introduces the flush_reg_cache command. It flushes all registers and can optionally invalidates the register cache after the flush. For targets which implement the register cache should implement the flush() callback in struct reg_arch_type. This functionality is also useful for test purposes. Example: - In RISC-V, some registers are WARL (write any read legal) and this command allows to check this behavior. We plan to implement the corresponding callback in the RISC-V target. Change-Id: I9537a5f05b46330f70aad17f77b2b80dedad068a Signed-off-by: Marek Vrbka Signed-off-by: Jan Matyas --- doc/openocd.texi | 26 +++-- src/target/arc.c | 1 + src/target/armv4_5.c | 1 + src/target/armv7m.c | 1 + src/target/armv8.c | 2 + src/target/avr32_ap7k.c | 1 + src/target/cortex_m.c | 1 + src/target/dsp563xx.c | 1 + src/target/embeddedice.c | 1 + src/target/esirisc.c | 1 + src/target/etb.c | 1 + src/target/etm.c | 1 + src/target/lakemont.c | 1 + src/target/mem_ap.c | 1 + src/target/mips32.c | 1 + src/target/mips64.c | 1 + src/target/openrisc/or1k.c | 1 + src/target/register.c | 59 ++++++++++ src/target/register.h | 4 + src/target/riscv/riscv-011_reg.c | 3 +- src/target/riscv/riscv-013_reg.c | 3 +- src/target/stm8.c | 1 + src/target/target.c | 187 ++++++++++++++++++++++++++++--- src/target/target_type.h | 4 + src/target/xscale.c | 1 + src/target/xtensa/xtensa.c | 1 + 26 files changed, 280 insertions(+), 26 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 1278873657..8facedfd1a 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -9135,7 +9135,7 @@ various operations. The current target may be changed by using @command{targets} command with the name of the target which should become current. -@deffn {Command} {reg} [(number|name) [(value|'force')]] +@deffn {Command} {reg} [(number|name)] [value] ['force'] Access a single register by @var{number} or by its @var{name}. The target must generally be halted before access to CPU core registers is allowed. Depending on the hardware, some other @@ -9149,15 +9149,17 @@ which are also dirty (and will be written back later) are flagged as such. @emph{With number/name}: display that register's value. -Use @var{force} argument to read directly from the target, -bypassing any internal cache. +Use @var{force} argument to read directly from the target +(as opposed to OpenOCD's internal register cache). @emph{With both number/name and value}: set register's value. Writes may be held in a writeback cache internal to OpenOCD, so that setting the value marks the register as dirty instead of immediately flushing that value. Resuming CPU execution (including by single stepping) or otherwise activating the -relevant module will flush such values. +relevant module will flush such values. The use of @var{force} +causes the register value to be written to the target +immediately. Cores may have surprisingly many registers in their Debug and trace infrastructure: @@ -9174,10 +9176,13 @@ Debug and trace infrastructure: @end example @end deffn -@deffn {Command} {set_reg} dict -Set register values of the target. +@deffn {Command} {set_reg} ['-force'] dict +Set register values of the target. The new register values may be +kept in OpenOCD's cache and not written to the target immediately +(unless @var{-force} is used). @itemize +@item @option{-force} ... Force immediate write of the new register values. @item @var{dict} ... Tcl dictionary with pairs of register names and values. @end itemize @@ -9193,7 +9198,7 @@ set_reg @{pc 0 sp 0x1000@} Get register values from the target and return them as Tcl dictionary with pairs of register names and values. If option "-force" is set, the register values are read directly from the -target, bypassing any caching. +target, not from OpenOCD's internal cache. @itemize @item @var{list} ... List of register names @@ -9207,6 +9212,13 @@ get_reg @{pc sp@} @end example @end deffn +@deffn {Command} {flush_reg_cache} [-invalidate] +Flush the internal OpenOCD's register cache - write back the dirty register values to the target. +If @option{-invalidate} is set, also invalidate (forget) the OpenOCD's cached register values; +therefore the next call to get_reg is guaranteed to read the fresh register value directly +from the target. +@end deffn + @deffn {Command} {write_memory} address width data ['phys'] This function provides an efficient way to write to the target memory from a Tcl script. diff --git a/src/target/arc.c b/src/target/arc.c index 28ce939473..1550dd72e8 100644 --- a/src/target/arc.c +++ b/src/target/arc.c @@ -293,6 +293,7 @@ static int arc_set_register(struct reg *reg, uint8_t *buf) static const struct reg_arch_type arc_reg_type = { .get = arc_get_register, .set = arc_set_register, + .flush = NULL, }; /* GDB register groups. For now we support only general and "empty" */ diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index c1836bc7ae..6708dd97ee 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -641,6 +641,7 @@ static int armv4_5_set_core_reg(struct reg *reg, uint8_t *buf) static const struct reg_arch_type arm_reg_type = { .get = armv4_5_get_core_reg, .set = armv4_5_set_core_reg, + .flush = NULL, }; struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm) diff --git a/src/target/armv7m.c b/src/target/armv7m.c index a403b25a91..cc1c29b8e9 100644 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -757,6 +757,7 @@ int armv7m_arch_state(struct target *target) static const struct reg_arch_type armv7m_reg_type = { .get = armv7m_get_core_reg, .set = armv7m_set_core_reg, + .flush = NULL, }; /** Builds cache of architecturally defined registers. */ diff --git a/src/target/armv8.c b/src/target/armv8.c index 88534d9626..a4e9b913ff 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -1714,6 +1714,7 @@ static int armv8_set_core_reg(struct reg *reg, uint8_t *buf) static const struct reg_arch_type armv8_reg_type = { .get = armv8_get_core_reg, .set = armv8_set_core_reg, + .flush = NULL, }; static int armv8_get_core_reg32(struct reg *reg) @@ -1775,6 +1776,7 @@ static int armv8_set_core_reg32(struct reg *reg, uint8_t *buf) static const struct reg_arch_type armv8_reg32_type = { .get = armv8_get_core_reg32, .set = armv8_set_core_reg32, + .flush = NULL, }; /** Builds cache of architecturally defined registers. */ diff --git a/src/target/avr32_ap7k.c b/src/target/avr32_ap7k.c index bbbf236592..2e5aa52b5d 100644 --- a/src/target/avr32_ap7k.c +++ b/src/target/avr32_ap7k.c @@ -157,6 +157,7 @@ static int avr32_set_core_reg(struct reg *reg, uint8_t *buf) static const struct reg_arch_type avr32_reg_type = { .get = avr32_get_core_reg, .set = avr32_set_core_reg, + .flush = NULL, }; static struct reg_cache *avr32_build_reg_cache(struct target *target) diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index fa95fcbc7e..d9bfdbc8a2 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -2429,6 +2429,7 @@ static const struct dwt_reg dwt_comp[] = { static const struct reg_arch_type dwt_reg_type = { .get = cortex_m_dwt_get_reg, .set = cortex_m_dwt_set_reg, + .flush = NULL, }; static void cortex_m_dwt_addreg(struct target *t, struct reg *r, const struct dwt_reg *d) diff --git a/src/target/dsp563xx.c b/src/target/dsp563xx.c index 0547947ef2..bfa7a22f03 100644 --- a/src/target/dsp563xx.c +++ b/src/target/dsp563xx.c @@ -430,6 +430,7 @@ static int dsp563xx_set_core_reg(struct reg *reg, uint8_t *buf) static const struct reg_arch_type dsp563xx_reg_type = { .get = dsp563xx_get_core_reg, .set = dsp563xx_set_core_reg, + .flush = NULL, }; static void dsp563xx_build_reg_cache(struct target *target) diff --git a/src/target/embeddedice.c b/src/target/embeddedice.c index 7aef153b58..5a0a7246ee 100644 --- a/src/target/embeddedice.c +++ b/src/target/embeddedice.c @@ -152,6 +152,7 @@ static int embeddedice_get_reg(struct reg *reg) static const struct reg_arch_type eice_reg_type = { .get = embeddedice_get_reg, .set = embeddedice_set_reg_w_exec, + .flush = NULL, }; /** diff --git a/src/target/esirisc.c b/src/target/esirisc.c index 6111414398..7ccab8580d 100644 --- a/src/target/esirisc.c +++ b/src/target/esirisc.c @@ -1420,6 +1420,7 @@ static int esirisc_set_reg(struct reg *reg, uint8_t *buf) static const struct reg_arch_type esirisc_reg_type = { .get = esirisc_get_reg, .set = esirisc_set_reg, + .flush = NULL, }; static struct reg_cache *esirisc_build_reg_cache(struct target *target) diff --git a/src/target/etb.c b/src/target/etb.c index 3b9004bb85..0b10d074d9 100644 --- a/src/target/etb.c +++ b/src/target/etb.c @@ -108,6 +108,7 @@ static int etb_get_reg(struct reg *reg) static const struct reg_arch_type etb_reg_type = { .get = etb_get_reg, .set = etb_set_reg_w_exec, + .flush = NULL, }; struct reg_cache *etb_build_reg_cache(struct etb *etb) diff --git a/src/target/etm.c b/src/target/etm.c index 53d5cb68ca..645ef57a11 100644 --- a/src/target/etm.c +++ b/src/target/etm.c @@ -215,6 +215,7 @@ static int etm_write_reg(struct reg *reg, uint32_t value); static const struct reg_arch_type etm_scan6_type = { .get = etm_get_reg, .set = etm_set_reg_w_exec, + .flush = NULL, }; /* Look up register by ID ... most ETM instances only diff --git a/src/target/lakemont.c b/src/target/lakemont.c index 0340d0d0b1..73fe4840a5 100644 --- a/src/target/lakemont.c +++ b/src/target/lakemont.c @@ -357,6 +357,7 @@ static const struct reg_arch_type lakemont_reg_type = { */ .get = lakemont_get_core_reg, .set = lakemont_set_core_reg, + .flush = NULL, }; struct reg_cache *lakemont_build_reg_cache(struct target *t) diff --git a/src/target/mem_ap.c b/src/target/mem_ap.c index 4114020776..7191a2f6e3 100644 --- a/src/target/mem_ap.c +++ b/src/target/mem_ap.c @@ -179,6 +179,7 @@ static int mem_ap_reg_set(struct reg *reg, uint8_t *buf) static struct reg_arch_type mem_ap_reg_arch_type = { .get = mem_ap_reg_get, .set = mem_ap_reg_set, + .flush = NULL, }; static const char *mem_ap_get_gdb_arch(const struct target *target) diff --git a/src/target/mips32.c b/src/target/mips32.c index dd40558a18..8854d56770 100644 --- a/src/target/mips32.c +++ b/src/target/mips32.c @@ -496,6 +496,7 @@ int mips32_arch_state(struct target *target) static const struct reg_arch_type mips32_reg_type = { .get = mips32_get_core_reg, .set = mips32_set_core_reg, + .flush = NULL, }; struct reg_cache *mips32_build_reg_cache(struct target *target) diff --git a/src/target/mips64.c b/src/target/mips64.c index 3d193a3009..9fcadc62e4 100644 --- a/src/target/mips64.c +++ b/src/target/mips64.c @@ -370,6 +370,7 @@ int mips64_arch_state(struct target *target) static const struct reg_arch_type mips64_reg_type = { .get = mips64_get_core_reg, .set = mips64_set_core_reg, + .flush = NULL, }; int mips64_build_reg_cache(struct target *target) diff --git a/src/target/openrisc/or1k.c b/src/target/openrisc/or1k.c index 8c38610805..f177c49aa0 100644 --- a/src/target/openrisc/or1k.c +++ b/src/target/openrisc/or1k.c @@ -494,6 +494,7 @@ static int or1k_set_core_reg(struct reg *reg, uint8_t *buf) static const struct reg_arch_type or1k_reg_type = { .get = or1k_get_core_reg, .set = or1k_set_core_reg, + .flush = NULL, }; static struct reg_cache *or1k_build_reg_cache(struct target *target) diff --git a/src/target/register.c b/src/target/register.c index e4f22f8e93..4dd7a3c028 100644 --- a/src/target/register.c +++ b/src/target/register.c @@ -115,12 +115,71 @@ static int register_set_dummy_core_reg(struct reg *reg, uint8_t *buf) return ERROR_OK; } +static int register_flush_dummy(struct reg *reg) +{ + reg->dirty = false; + + return ERROR_OK; +} + static const struct reg_arch_type dummy_type = { .get = register_get_dummy_core_reg, .set = register_set_dummy_core_reg, + .flush = register_flush_dummy, }; void register_init_dummy(struct reg *reg) { reg->type = &dummy_type; } + +int register_flush(struct reg *reg, bool invalidate) +{ + if (!reg) { + LOG_ERROR("BUG: %s called with NULL!", __func__); + return ERROR_FAIL; + } + + if (!reg->exist) { + LOG_ERROR("BUG: %s called with non-existent register!", __func__); + return ERROR_FAIL; + } + + if (!reg->dirty) { + LOG_DEBUG("Register '%s' is not dirty, nothing to flush", reg->name); + if (reg->valid && invalidate) { + LOG_DEBUG("Invalidating register '%s'", reg->name); + reg->valid = false; + } + return ERROR_OK; + } + + if (!reg->type->flush) { + LOG_ERROR("Unable to flush dirty register '%s', operation is not supported", reg->name); + return ERROR_NOT_IMPLEMENTED; + } + + if (!reg->valid) { + LOG_ERROR("BUG: Register '%s' is not valid, but flush attempted", reg->name); + return ERROR_FAIL; + } + + LOG_DEBUG("Flushing register '%s'.", reg->name); + + int result = reg->type->flush(reg); + if (result != ERROR_OK) { + LOG_ERROR("Failed to flush register '%s'", reg->name); + return result; + } + + if (reg->dirty) { + LOG_ERROR("BUG: Register '%s' remained dirty after flushing!", reg->name); + return ERROR_FAIL; + } + if (reg->valid && invalidate) { + LOG_DEBUG("Invalidating register '%s' after flush", reg->name); + reg->valid = false; + } + + return ERROR_OK; +} diff --git a/src/target/register.h b/src/target/register.h index b9af401b4c..93f21059af 100644 --- a/src/target/register.h +++ b/src/target/register.h @@ -151,6 +151,7 @@ struct reg_cache { struct reg_arch_type { int (*get)(struct reg *reg); int (*set)(struct reg *reg, uint8_t *buf); + int (*flush)(struct reg *reg); }; struct reg *register_get_by_number(struct reg_cache *first, @@ -163,4 +164,7 @@ void register_cache_invalidate(struct reg_cache *cache); void register_init_dummy(struct reg *reg); +/* Flushes the register. Invalidates the register if invalidate == true */ +int register_flush(struct reg *reg, bool invalidate); + #endif /* OPENOCD_TARGET_REGISTER_H */ diff --git a/src/target/riscv/riscv-011_reg.c b/src/target/riscv/riscv-011_reg.c index 44ea1a49a4..8c48b6fa50 100644 --- a/src/target/riscv/riscv-011_reg.c +++ b/src/target/riscv/riscv-011_reg.c @@ -31,7 +31,8 @@ static const struct reg_arch_type *riscv011_gdb_regno_reg_type(uint32_t regno) { static const struct reg_arch_type riscv011_reg_type = { .get = riscv011_reg_get, - .set = riscv011_reg_set + .set = riscv011_reg_set, + .flush = NULL }; return &riscv011_reg_type; } diff --git a/src/target/riscv/riscv-013_reg.c b/src/target/riscv/riscv-013_reg.c index 514103b2da..f1fea7401c 100644 --- a/src/target/riscv/riscv-013_reg.c +++ b/src/target/riscv/riscv-013_reg.c @@ -77,7 +77,8 @@ static const struct reg_arch_type *riscv013_gdb_regno_reg_type(uint32_t regno) { static const struct reg_arch_type riscv013_reg_type = { .get = riscv013_reg_get, - .set = riscv013_reg_set + .set = riscv013_reg_set, + .flush = NULL }; return &riscv013_reg_type; } diff --git a/src/target/stm8.c b/src/target/stm8.c index 2b3466dacb..e5a0eb46d6 100644 --- a/src/target/stm8.c +++ b/src/target/stm8.c @@ -1182,6 +1182,7 @@ static int stm8_get_gdb_reg_list(struct target *target, struct reg **reg_list[], static const struct reg_arch_type stm8_reg_type = { .get = stm8_get_core_reg, .set = stm8_set_core_reg, + .flush = NULL, }; static struct reg_cache *stm8_build_reg_cache(struct target *target) diff --git a/src/target/target.c b/src/target/target.c index fb038311bf..5983ddfa3b 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -1470,6 +1470,85 @@ unsigned int target_data_bits(struct target *target) return 32; } +static bool target_implements_reg_flush(const struct reg_cache *cache) +{ + while (cache) { + for (unsigned int i = 0; i < cache->num_regs; i++) { + struct reg *reg = &cache->reg_list[i]; + if (reg->type->flush) + return true; + } + cache = cache->next; + } + return false; +} + +static int target_flush_all_regs_default(struct target *target, bool invalidate) +{ + struct reg_cache *cache = target->reg_cache; + + // In targets where per-register flushing isn't implemented yet + // this function will result in many warnings, these two if branches + // are here to prevent them + if (!target_implements_reg_flush(cache)) { + LOG_TARGET_ERROR(target, "Unable to flush register cache - operation not supported by the target."); + return ERROR_FAIL; + } + + unsigned int total_count = 0; + unsigned int dirty_count = 0; + unsigned int failed_count = 0; + while (cache) { + for (unsigned int i = 0; i < cache->num_regs; i++) { + total_count++; + struct reg *reg = &cache->reg_list[i]; + + if (!reg->exist || !reg->valid) + continue; + + if (reg->dirty) { + dirty_count++; + if (!reg->type->flush) { + LOG_TARGET_WARNING(target, "Flushing register '%s' is not implemented", reg->name); + failed_count++; + continue; + } + LOG_TARGET_DEBUG(target, "Flushing register %s", reg->name); + int result = reg->type->flush(reg); + if (result != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to flush register '%s'", reg->name); + failed_count++; + continue; + } + if (reg->dirty) { + LOG_TARGET_ERROR(target, "BUG: Register '%s' remains dirty after flushing", reg->name); + return ERROR_FAIL; + } + } + if (invalidate) { + LOG_TARGET_DEBUG(target, "Invalidating register '%s'", reg->name); + reg->valid = false; + } + } + cache = cache->next; + } + + if (dirty_count == 0) { + LOG_TARGET_INFO(target, "No registers dirty, nothing to flush."); + return ERROR_OK; + } + + LOG_TARGET_DEBUG(target, "Found %u dirty registers out of %u all registers.", dirty_count, total_count); + if (failed_count > 0) { + LOG_TARGET_ERROR(target, "Failed to flush %u out of %u dirty registers.", + failed_count, dirty_count); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "All dirty registers flushed."); + + return ERROR_OK; +} + static int target_profiling(struct target *target, uint32_t *samples, uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds) { @@ -1536,6 +1615,9 @@ static int target_init_one(struct command_context *cmd_ctx, if (!target->type->profiling) target->type->profiling = target_profiling_default; + if (!target->type->flush_all_regs) + target->type->flush_all_regs = target_flush_all_regs_default; + return ERROR_OK; } @@ -3106,13 +3188,22 @@ COMMAND_HANDLER(handle_reg_command) /* display a register */ if ((CMD_ARGC == 1) || ((CMD_ARGC == 2) && !((CMD_ARGV[1][0] >= '0') && (CMD_ARGV[1][0] <= '9')))) { - if ((CMD_ARGC == 2) && (strcmp(CMD_ARGV[1], "force") == 0)) - reg->valid = false; + if (CMD_ARGC == 2 + && strcmp(CMD_ARGV[1], "force") == 0 + && reg->valid + // TODO: Remove this check in the future once all targets implement register flushing + && target_implements_reg_flush(target->reg_cache)) { + int retval = register_flush(reg, true); + if (retval != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to flush register '%s' before force-reading", reg->name); + return retval; + } + } if (!reg->valid) { int retval = reg->type->get(reg); if (retval != ERROR_OK) { - LOG_ERROR("Could not read register '%s'", reg->name); + LOG_TARGET_ERROR(target, "Could not read register '%s'", reg->name); return retval; } } @@ -3123,7 +3214,14 @@ COMMAND_HANDLER(handle_reg_command) } /* set register value */ - if (CMD_ARGC == 2) { + if (CMD_ARGC >= 2 && CMD_ARGC <= 3) { + bool flush = false; + + if (CMD_ARGC >= 3 && !strcmp(CMD_ARGV[2], "force")) + flush = true; + else if (CMD_ARGC >= 3) + return ERROR_COMMAND_SYNTAX_ERROR; + uint8_t *buf = malloc(DIV_ROUND_UP(reg->size, 8)); if (!buf) { LOG_ERROR("Failed to allocate memory"); @@ -3137,17 +3235,20 @@ COMMAND_HANDLER(handle_reg_command) } retval = reg->type->set(reg, buf); + free(buf); if (retval != ERROR_OK) { LOG_ERROR("Could not write to register '%s'", reg->name); + return retval; } else { char *value = buf_to_hex_str(reg->value, reg->size); command_print(CMD, "%s (/%i): 0x%s", reg->name, (int)(reg->size), value); free(value); } - free(buf); + if (reg->dirty && flush) + return register_flush(reg, false); - return retval; + return ERROR_OK; } return ERROR_COMMAND_SYNTAX_ERROR; @@ -4742,6 +4843,14 @@ static int target_jim_get_reg(Jim_Interp *interp, int argc, return JIM_ERR; } + if (force && reg->valid && reg->dirty) { + int retval = register_flush(reg, false); + if (retval != ERROR_OK) { + Jim_SetResultFormatted(interp, "failed to flush dirty register '%s' before force-reading", reg->name); + return retval; + } + } + if (force || !reg->valid) { int retval = reg->type->get(reg); @@ -4781,18 +4890,30 @@ static int target_jim_get_reg(Jim_Interp *interp, int argc, COMMAND_HANDLER(handle_set_reg_command) { - if (CMD_ARGC != 1) + if (CMD_ARGC < 1 || CMD_ARGC > 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + bool flush = false; + const char *force_arg = NULL; + + if (CMD_ARGC == 3) + force_arg = CMD_ARGV[0]; + + if (force_arg && !strcmp(force_arg, "-force")) { + flush = true; + } else if (force_arg) { return ERROR_COMMAND_SYNTAX_ERROR; + } int tmp; #if JIM_VERSION >= 80 - Jim_Obj **dict = Jim_DictPairs(CMD_CTX->interp, CMD_JIMTCL_ARGV[0], &tmp); + Jim_Obj **dict = Jim_DictPairs(CMD_CTX->interp, CMD_JIMTCL_ARGV[CMD_ARGC - 1], &tmp); if (!dict) return ERROR_FAIL; #else Jim_Obj **dict; - int ret = Jim_DictPairs(CMD_CTX->interp, CMD_JIMTCL_ARGV[0], &dict, &tmp); + int ret = Jim_DictPairs(CMD_CTX->interp, CMD_JIMTCL_ARGV[CMD_ARGC - 1], &dict, &tmp); if (ret != JIM_OK) return ERROR_FAIL; @@ -4833,6 +4954,14 @@ COMMAND_HANDLER(handle_set_reg_command) reg_value, reg_name); return retval; } + + if (flush && reg->valid && reg->dirty) { + retval = register_flush(reg, false); + if (retval != ERROR_OK) { + command_print(CMD, "Failed to flush register '%s'", reg->name); + return retval; + } + } } return ERROR_OK; @@ -5570,14 +5699,14 @@ static const struct command_registration target_instance_command_handlers[] = { .mode = COMMAND_EXEC, .jim_handler = target_jim_get_reg, .help = "Get register values from the target", - .usage = "list", + .usage = "['-force'] list", }, { .name = "set_reg", .mode = COMMAND_EXEC, .handler = handle_set_reg_command, .help = "Set target register values", - .usage = "dict", + .usage = "['-force'] dict", }, { .name = "read_memory", @@ -6492,6 +6621,24 @@ COMMAND_HANDLER(handle_test_mem_access_command) return retval; } +COMMAND_HANDLER(handle_flush_reg_cache_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + bool invalidate = false; + + if (CMD_ARGC == 1) { + if (strcmp(CMD_ARGV[0], "-invalidate")) + return ERROR_COMMAND_SYNTAX_ERROR; + invalidate = true; + } + + return target->type->flush_all_regs(target, invalidate); +} + static const struct command_registration target_exec_command_handlers[] = { { .name = "fast_load_image", @@ -6529,9 +6676,9 @@ static const struct command_registration target_exec_command_handlers[] = { .name = "reg", .handler = handle_reg_command, .mode = COMMAND_EXEC, - .help = "display (reread from target with \"force\") or set a register; " - "with no arguments, displays all registers and their values", - .usage = "[(register_number|register_name) [(value|'force')]]", + .help = "display (reread from target with \"force\") or set a register " + "(write to the target immediately with \"force\"); ", + .usage = "[(register_number|register_name) [value] ['force']]", }, { .name = "poll", @@ -6705,14 +6852,21 @@ static const struct command_registration target_exec_command_handlers[] = { .mode = COMMAND_EXEC, .jim_handler = target_jim_get_reg, .help = "Get register values from the target", - .usage = "list", + .usage = "['-force'] list", }, { .name = "set_reg", .mode = COMMAND_EXEC, .handler = handle_set_reg_command, .help = "Set target register values", - .usage = "dict", + .usage = "['-force'] dict", + }, + { + .name = "flush_reg_cache", + .handler = handle_flush_reg_cache_command, + .mode = COMMAND_EXEC, + .help = "Flush register cache", + .usage = "['-invalidate']", }, { .name = "read_memory", @@ -6757,7 +6911,6 @@ static const struct command_registration target_exec_command_handlers[] = { .help = "Test the target's memory access functions", .usage = "size", }, - COMMAND_REGISTRATION_DONE }; static int target_register_user_commands(struct command_context *cmd_ctx) diff --git a/src/target/target_type.h b/src/target/target_type.h index f35a59c5fe..7f85eaeaf5 100644 --- a/src/target/target_type.h +++ b/src/target/target_type.h @@ -309,6 +309,10 @@ struct target_type { * will typically be 32 for 32-bit targets, and 64 for 64-bit targets. If * not implemented, it's assumed to be 32. */ unsigned int (*data_bits)(struct target *target); + + /* Flushes the registers cache. + * Also invalidate flushed registers if invalidate == true */ + int (*flush_all_regs)(struct target *target, bool invalidate); }; extern struct target_type aarch64_target; diff --git a/src/target/xscale.c b/src/target/xscale.c index 155abaef1a..b45e7cc0aa 100644 --- a/src/target/xscale.c +++ b/src/target/xscale.c @@ -2854,6 +2854,7 @@ static int xscale_analyze_trace(struct target *target, struct command_invocation static const struct reg_arch_type xscale_reg_type = { .get = xscale_get_reg, .set = xscale_set_reg, + .flush = NULL, }; static void xscale_build_reg_cache(struct target *target) diff --git a/src/target/xtensa/xtensa.c b/src/target/xtensa/xtensa.c index fe1b83bc3e..8aad1784c1 100644 --- a/src/target/xtensa/xtensa.c +++ b/src/target/xtensa/xtensa.c @@ -490,6 +490,7 @@ static int xtensa_core_reg_set(struct reg *reg, uint8_t *buf) static const struct reg_arch_type xtensa_reg_type = { .get = xtensa_core_reg_get, .set = xtensa_core_reg_set, + .flush = NULL, }; /* Convert a register index that's indexed relative to windowbase, to the real address. */