[CHERRY PICK] target: Fix force-reading of registers and add flush capability
Cherrry-picked form https://review.openocd.org/c/openocd/+/8070/21 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 <name> 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 invalidate the register cache after the flush. For targets which implement the register cache, the flush() callback in struct reg_arch_type should be implemented (in separate patches, by the maintainers of each of the target 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 <marek.vrbka@codasip.com> Signed-off-by: Jan Matyas <jan.matyas@codasip.com>
This commit is contained in:
parent
fa7e2351c8
commit
2c4dfd9d61
|
@ -9271,7 +9271,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
|
||||
|
@ -9285,15 +9285,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:
|
||||
|
@ -9310,10 +9312,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
|
||||
|
||||
|
@ -9329,7 +9334,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
|
||||
|
@ -9343,6 +9348,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.
|
||||
|
|
|
@ -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" */
|
||||
|
|
|
@ -655,6 +655,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)
|
||||
|
|
|
@ -786,6 +786,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. */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -2431,6 +2431,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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1419,6 +1419,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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "register.h"
|
||||
#include <helper/log.h>
|
||||
#include <target/target.h>
|
||||
#include <target/target_type.h>
|
||||
|
||||
/**
|
||||
* @file
|
||||
|
@ -115,12 +117,72 @@ 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(const struct target *target, 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_TARGET_DEBUG(target, "Register '%s' is not dirty, nothing to flush", reg->name);
|
||||
if (reg->valid && invalidate) {
|
||||
LOG_TARGET_DEBUG(target, "Invalidating register '%s'", reg->name);
|
||||
reg->valid = false;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (!reg->type->flush) {
|
||||
LOG_TARGET_ERROR(target, "Unable to flush dirty register '%s' - operation not yet supported "
|
||||
"by %s implementation in OpenOCD", reg->name, target->type->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_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);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (reg->dirty) {
|
||||
LOG_ERROR("BUG: Register '%s' remained dirty after flushing", reg->name);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (reg->valid && invalidate) {
|
||||
LOG_TARGET_DEBUG(target, "Invalidating register '%s' after flush", reg->name);
|
||||
reg->valid = false;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
|
|
@ -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. Also invalidates the cached register value if invalidate == true */
|
||||
int register_flush(const struct target *target, struct reg *reg, bool invalidate);
|
||||
|
||||
#endif /* OPENOCD_TARGET_REGISTER_H */
|
||||
|
|
|
@ -1180,6 +1180,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)
|
||||
|
|
|
@ -1470,6 +1470,86 @@ 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
|
||||
* would result in many errors (one for each dirty register). The check below is
|
||||
* to prevent that, and just a single clear error message will be printed. */
|
||||
if (!target_implements_reg_flush(cache)) {
|
||||
LOG_TARGET_ERROR(target, "Unable to flush register cache - operation not yet supported "
|
||||
"by %s implementation in OpenOCD", target->type->name);
|
||||
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 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 +1616,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,16 +3189,24 @@ 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) {
|
||||
int retval = register_flush(target, 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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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,21 @@ 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(target, reg, false);
|
||||
|
||||
return retval;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
@ -4742,6 +4844,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(target, 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 +4891,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;
|
||||
|
||||
/* Check for the "-force" argument */
|
||||
if (CMD_ARGC == 2) {
|
||||
if (!strcmp(CMD_ARGV[0], "-force")) {
|
||||
flush = true;
|
||||
} else {
|
||||
command_print(CMD, "unknown argument '%s'", CMD_ARGV[0]);
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
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 +4955,14 @@ COMMAND_HANDLER(handle_set_reg_command)
|
|||
reg_value, reg_name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (flush && reg->valid && reg->dirty) {
|
||||
retval = register_flush(target, reg, false);
|
||||
if (retval != ERROR_OK) {
|
||||
Jim_SetResultFormatted(CMD_CTX->interp, "Failed to flush register '%s'", reg->name);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
|
@ -5570,14 +5700,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",
|
||||
|
@ -6495,6 +6625,24 @@ nextw:
|
|||
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",
|
||||
|
@ -6532,9 +6680,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",
|
||||
|
@ -6708,14 +6856,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",
|
||||
|
@ -6760,7 +6915,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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
/* Flush the whole register cache.
|
||||
* Also invalidate flushed register values if invalidate == true */
|
||||
int (*flush_all_regs)(struct target *target, bool invalidate);
|
||||
};
|
||||
|
||||
extern struct target_type aarch64_target;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in New Issue