Skip to content

Commit de20c2a

Browse files
committed
target/riscv: clean-up register invalidation
* Registers were not invalidated if the hart became unavailable. * Improved logging in the case register invalidation involves loss of information. Change-Id: Icfb5e190dd6dcb1a97e4d314d802466cab7a01e4 Signed-off-by: Evgeniy Naydanov <[email protected]>
1 parent ea8f9d5 commit de20c2a

File tree

5 files changed

+80
-21
lines changed

5 files changed

+80
-21
lines changed

src/target/riscv/riscv-011.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,7 @@ static int execute_resume(struct target *target, bool step)
11361136
}
11371137

11381138
target->state = TARGET_RUNNING;
1139-
register_cache_invalidate(target->reg_cache);
1139+
riscv_reg_cache_invalidate_all(target);
11401140

11411141
return ERROR_OK;
11421142
}

src/target/riscv/riscv-013.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2728,6 +2728,13 @@ static int handle_became_unavailable(struct target *target,
27282728
enum riscv_hart_state previous_riscv_state)
27292729
{
27302730
RISCV013_INFO(info);
2731+
2732+
if (riscv_reg_cache_any_dirty(target, LOG_LVL_WARNING))
2733+
LOG_TARGET_WARNING(target, "Discarding values of dirty registers "
2734+
"(due to target becoming unavailable).");
2735+
2736+
riscv_reg_cache_invalidate_all(target);
2737+
27312738
info->dcsr_ebreak_is_set = false;
27322739
return ERROR_OK;
27332740
}
@@ -5435,6 +5442,8 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
54355442
if (riscv_reg_flush_all(target) != ERROR_OK)
54365443
return ERROR_FAIL;
54375444

5445+
riscv_reg_cache_invalidate_all(target);
5446+
54385447
dm013_info_t *dm = get_dm(target);
54395448
/* Issue the resume command, and then wait for the current hart to resume. */
54405449
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ;

src/target/riscv/riscv.c

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,6 @@ static const virt2phys_info_t sv57x4 = {
290290

291291
static enum riscv_halt_reason riscv_halt_reason(struct target *target);
292292
static void riscv_info_init(struct target *target, struct riscv_info *r);
293-
static void riscv_invalidate_register_cache(struct target *target);
294293
static int riscv_step_rtos_hart(struct target *target);
295294

296295
static void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before)
@@ -2485,10 +2484,15 @@ static int riscv_halt_go_all_harts(struct target *target)
24852484
return ERROR_FAIL;
24862485
}
24872486
} else {
2487+
// Safety check:
2488+
if (riscv_reg_cache_any_dirty(target, LOG_LVL_ERROR))
2489+
LOG_TARGET_INFO(target, "BUG: Registers should not be dirty while "
2490+
"the target is not halted!");
2491+
2492+
riscv_reg_cache_invalidate_all(target);
2493+
24882494
if (r->halt_go(target) != ERROR_OK)
24892495
return ERROR_FAIL;
2490-
2491-
riscv_invalidate_register_cache(target);
24922496
}
24932497

24942498
return ERROR_OK;
@@ -2572,7 +2576,11 @@ static int riscv_assert_reset(struct target *target)
25722576
struct target_type *tt = get_target_type(target);
25732577
if (!tt)
25742578
return ERROR_FAIL;
2575-
riscv_invalidate_register_cache(target);
2579+
2580+
if (riscv_reg_cache_any_dirty(target, LOG_LVL_INFO))
2581+
LOG_TARGET_INFO(target, "Discarding values of dirty registers.");
2582+
2583+
riscv_reg_cache_invalidate_all(target);
25762584
return tt->assert_reset(target);
25772585
}
25782586

@@ -2699,7 +2707,15 @@ static int resume_go(struct target *target, int current,
26992707
static int resume_finish(struct target *target, int debug_execution)
27002708
{
27012709
assert(target->state == TARGET_HALTED);
2702-
register_cache_invalidate(target->reg_cache);
2710+
if (riscv_reg_cache_any_dirty(target, LOG_LVL_ERROR)) {
2711+
/* If this happens, it means there is a bug in the previous
2712+
* register-flushing algorithm: not all registers were flushed
2713+
* back to the target in preparation for the resume.*/
2714+
LOG_TARGET_ERROR(target,
2715+
"BUG: registers should have been flushed by this point.");
2716+
}
2717+
2718+
riscv_reg_cache_invalidate_all(target);
27032719

27042720
target->state = debug_execution ? TARGET_DEBUG_RUNNING : TARGET_RUNNING;
27052721
target->debug_reason = DBG_REASON_NOTHALTED;
@@ -4001,7 +4017,15 @@ static int riscv_openocd_step_impl(struct target *target, int current,
40014017
LOG_TARGET_ERROR(target, "Unable to step rtos hart.");
40024018
}
40034019

4004-
register_cache_invalidate(target->reg_cache);
4020+
if (riscv_reg_cache_any_dirty(target, LOG_LVL_ERROR)) {
4021+
/* If this happens, it means there is a bug in the previous
4022+
* register-flushing algorithm: not all registers were flushed
4023+
* back to the target prior to single-step. */
4024+
LOG_TARGET_ERROR(target,
4025+
"BUG: registers should have been flushed by this point.");
4026+
}
4027+
4028+
riscv_reg_cache_invalidate_all(target);
40054029

40064030
if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY)
40074031
if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) {
@@ -5177,7 +5201,7 @@ COMMAND_HANDLER(riscv_exec_progbuf)
51775201
if (riscv_reg_flush_all(target) != ERROR_OK)
51785202
return ERROR_FAIL;
51795203
int error = riscv_program_exec(&prog, target);
5180-
riscv_invalidate_register_cache(target);
5204+
riscv_reg_cache_invalidate_all(target);
51815205

51825206
if (error != ERROR_OK) {
51835207
LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer execution failed.");
@@ -5731,8 +5755,6 @@ static int riscv_resume_go_all_harts(struct target *target)
57315755
} else {
57325756
LOG_TARGET_DEBUG(target, "Hart requested resume, but was already resumed.");
57335757
}
5734-
5735-
riscv_invalidate_register_cache(target);
57365758
return ERROR_OK;
57375759
}
57385760

@@ -5826,17 +5848,6 @@ unsigned int riscv_vlenb(const struct target *target)
58265848
return r->vlenb;
58275849
}
58285850

5829-
static void riscv_invalidate_register_cache(struct target *target)
5830-
{
5831-
/* Do not invalidate the register cache if it is not yet set up
5832-
* (e.g. when the target failed to get examined). */
5833-
if (!target->reg_cache)
5834-
return;
5835-
5836-
LOG_TARGET_DEBUG(target, "Invalidating register cache.");
5837-
register_cache_invalidate(target->reg_cache);
5838-
}
5839-
58405851
int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state)
58415852
{
58425853
RISCV_INFO(r);

src/target/riscv/riscv_reg.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,34 @@ static int riscv_set_or_write_register(struct target *target,
888888
return ERROR_OK;
889889
}
890890

891+
bool riscv_reg_cache_any_dirty(const struct target *target, int log_level)
892+
{
893+
bool any_dirty = false;
894+
895+
if (!target->reg_cache)
896+
return any_dirty;
897+
898+
for (unsigned int number = 0; number < target->reg_cache->num_regs; ++number) {
899+
const struct reg * const reg = riscv_reg_impl_cache_entry(target, number);
900+
assert(riscv_reg_impl_is_initialized(reg));
901+
if (reg->dirty) {
902+
log_printf_lf(log_level, __FILE__, __LINE__, __func__,
903+
"[%s] Register %s is dirty!", target_name(target), reg->name);
904+
any_dirty = true;
905+
}
906+
}
907+
return any_dirty;
908+
}
909+
910+
void riscv_reg_cache_invalidate_all(struct target *target)
911+
{
912+
if (!target->reg_cache)
913+
return;
914+
915+
LOG_TARGET_DEBUG(target, "Invalidating register cache.");
916+
register_cache_invalidate(target->reg_cache);
917+
}
918+
891919
/**
892920
* This function is used to change the value of a register. The new value may
893921
* be cached, and may not be written until the hart is resumed.

src/target/riscv/riscv_reg.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ void riscv_reg_free_all(struct target *target);
2121

2222
/** Write all dirty registers to the target. */
2323
int riscv_reg_flush_all(struct target *target);
24+
/**
25+
* Check whether there are any dirty registers in the OpenOCD's register cache.
26+
* In addition, all dirty registers will be reported to the log using the
27+
* supplied "log_level".
28+
*/
29+
bool riscv_reg_cache_any_dirty(const struct target *target, int log_level);
30+
/**
31+
* Invalidate all registers - forget their cached register values.
32+
* WARNING: If a register was dirty, its walue will be silently lost!
33+
*/
34+
void riscv_reg_cache_invalidate_all(struct target *target);
2435
/**
2536
* Set the register value. For cacheable registers, only the cache is updated
2637
* (write-back mode).

0 commit comments

Comments
 (0)