diff options
Diffstat (limited to 'target/riscv/debug.c')
| -rw-r--r-- | target/riscv/debug.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/target/riscv/debug.c b/target/riscv/debug.c index e44848d0d7..bf4840a6a3 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -29,6 +29,8 @@ #include "cpu.h" #include "trace.h" #include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "sysemu/cpu-timers.h" /* * The following M-mode trigger CSRs are implemented: @@ -496,10 +498,209 @@ static void type6_reg_write(CPURISCVState *env, target_ulong index, return; } +/* icount trigger type */ +static inline int +itrigger_get_count(CPURISCVState *env, int index) +{ + return get_field(env->tdata1[index], ITRIGGER_COUNT); +} + +static inline void +itrigger_set_count(CPURISCVState *env, int index, int value) +{ + env->tdata1[index] = set_field(env->tdata1[index], + ITRIGGER_COUNT, value); +} + +static bool check_itrigger_priv(CPURISCVState *env, int index) +{ + target_ulong tdata1 = env->tdata1[index]; + if (riscv_cpu_virt_enabled(env)) { + /* check VU/VS bit against current privilege level */ + return (get_field(tdata1, ITRIGGER_VS) == env->priv) || + (get_field(tdata1, ITRIGGER_VU) == env->priv); + } else { + /* check U/S/M bit against current privilege level */ + return (get_field(tdata1, ITRIGGER_M) == env->priv) || + (get_field(tdata1, ITRIGGER_S) == env->priv) || + (get_field(tdata1, ITRIGGER_U) == env->priv); + } +} + +bool riscv_itrigger_enabled(CPURISCVState *env) +{ + int count; + for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { + continue; + } + if (check_itrigger_priv(env, i)) { + continue; + } + count = itrigger_get_count(env, i); + if (!count) { + continue; + } + return true; + } + + return false; +} + +void helper_itrigger_match(CPURISCVState *env) +{ + int count; + for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { + continue; + } + if (check_itrigger_priv(env, i)) { + continue; + } + count = itrigger_get_count(env, i); + if (!count) { + continue; + } + itrigger_set_count(env, i, count--); + if (!count) { + env->itrigger_enabled = riscv_itrigger_enabled(env); + do_trigger_action(env, i); + } + } +} + +static void riscv_itrigger_update_count(CPURISCVState *env) +{ + int count, executed; + /* + * Record last icount, so that we can evaluate the executed instructions + * since last priviledge mode change or timer expire. + */ + int64_t last_icount = env->last_icount, current_icount; + current_icount = env->last_icount = icount_get_raw(); + + for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { + continue; + } + count = itrigger_get_count(env, i); + if (!count) { + continue; + } + /* + * Only when priviledge is changed or itrigger timer expires, + * the count field in itrigger tdata1 register is updated. + * And the count field in itrigger only contains remaining value. + */ + if (check_itrigger_priv(env, i)) { + /* + * If itrigger enabled in this priviledge mode, the number of + * executed instructions since last priviledge change + * should be reduced from current itrigger count. + */ + executed = current_icount - last_icount; + itrigger_set_count(env, i, count - executed); + if (count == executed) { + do_trigger_action(env, i); + } + } else { + /* + * If itrigger is not enabled in this priviledge mode, + * the number of executed instructions will be discard and + * the count field in itrigger will not change. + */ + timer_mod(env->itrigger_timer[i], + current_icount + count); + } + } +} + +static void riscv_itrigger_timer_cb(void *opaque) +{ + riscv_itrigger_update_count((CPURISCVState *)opaque); +} + +void riscv_itrigger_update_priv(CPURISCVState *env) +{ + riscv_itrigger_update_count(env); +} + +static target_ulong itrigger_validate(CPURISCVState *env, + target_ulong ctrl) +{ + target_ulong val; + + /* validate the generic part first */ + val = tdata1_validate(env, ctrl, TRIGGER_TYPE_INST_CNT); + + /* validate unimplemented (always zero) bits */ + warn_always_zero_bit(ctrl, ITRIGGER_ACTION, "action"); + warn_always_zero_bit(ctrl, ITRIGGER_HIT, "hit"); + warn_always_zero_bit(ctrl, ITRIGGER_PENDING, "pending"); + + /* keep the mode and attribute bits */ + val |= ctrl & (ITRIGGER_VU | ITRIGGER_VS | ITRIGGER_U | ITRIGGER_S | + ITRIGGER_M | ITRIGGER_COUNT); + + return val; +} + +static void itrigger_reg_write(CPURISCVState *env, target_ulong index, + int tdata_index, target_ulong val) +{ + target_ulong new_val; + + switch (tdata_index) { + case TDATA1: + /* set timer for icount */ + new_val = itrigger_validate(env, val); + if (new_val != env->tdata1[index]) { + env->tdata1[index] = new_val; + if (icount_enabled()) { + env->last_icount = icount_get_raw(); + /* set the count to timer */ + timer_mod(env->itrigger_timer[index], + env->last_icount + itrigger_get_count(env, index)); + } else { + env->itrigger_enabled = riscv_itrigger_enabled(env); + } + } + break; + case TDATA2: + qemu_log_mask(LOG_UNIMP, + "tdata2 is not supported for icount trigger\n"); + break; + case TDATA3: + qemu_log_mask(LOG_UNIMP, + "tdata3 is not supported for icount trigger\n"); + break; + default: + g_assert_not_reached(); + } + + return; +} + +static int itrigger_get_adjust_count(CPURISCVState *env) +{ + int count = itrigger_get_count(env, env->trigger_cur), executed; + if ((count != 0) && check_itrigger_priv(env, env->trigger_cur)) { + executed = icount_get_raw() - env->last_icount; + count += executed; + } + return count; +} + target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index) { + int trigger_type; switch (tdata_index) { case TDATA1: + trigger_type = extract_trigger_type(env, env->tdata1[env->trigger_cur]); + if ((trigger_type == TRIGGER_TYPE_INST_CNT) && icount_enabled()) { + return deposit64(env->tdata1[env->trigger_cur], 10, 14, + itrigger_get_adjust_count(env)); + } return env->tdata1[env->trigger_cur]; case TDATA2: return env->tdata2[env->trigger_cur]; @@ -528,6 +729,8 @@ void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val) type6_reg_write(env, env->trigger_cur, tdata_index, val); break; case TRIGGER_TYPE_INST_CNT: + itrigger_reg_write(env, env->trigger_cur, tdata_index, val); + break; case TRIGGER_TYPE_INT: case TRIGGER_TYPE_EXCP: case TRIGGER_TYPE_EXT_SRC: @@ -725,5 +928,7 @@ void riscv_trigger_init(CPURISCVState *env) env->tdata3[i] = 0; env->cpu_breakpoint[i] = NULL; env->cpu_watchpoint[i] = NULL; + env->itrigger_timer[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL, + riscv_itrigger_timer_cb, env); } } |