pmu_check_overflow() will verify for overflow in the PMC1-5 counters, firing a performance monitor alert if an overflow happened with the proper MMCR0 bits set.
The alert is fired by using helper_pmu_overflow(). Signed-off-by: Daniel Henrique Barboza <danielhb...@gmail.com> --- target/ppc/helper.h | 1 + target/ppc/power8-pmu-insn-cnt.c.inc | 89 ++++++++++++++++++++++++++++ target/ppc/power8-pmu.c | 8 +++ 3 files changed, 98 insertions(+) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index fb6cac38b4..4d8193caab 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -26,6 +26,7 @@ DEF_HELPER_2(store_mmcr1, void, env, tl) DEF_HELPER_3(store_pmc, void, env, i32, i64) DEF_HELPER_2(read_pmc, tl, env, i32) DEF_HELPER_2(insns_inc, void, env, i32) +DEF_HELPER_1(pmu_overflow, void, env) #endif DEF_HELPER_1(check_tlb_flush_local, void, env) DEF_HELPER_1(check_tlb_flush_global, void, env) diff --git a/target/ppc/power8-pmu-insn-cnt.c.inc b/target/ppc/power8-pmu-insn-cnt.c.inc index d3dd6d4685..7bd07d8105 100644 --- a/target/ppc/power8-pmu-insn-cnt.c.inc +++ b/target/ppc/power8-pmu-insn-cnt.c.inc @@ -19,6 +19,7 @@ #define MMCR1_PMC4_INS_CNT 0x00000002 #define MMCR1_PMC4_INS_LATCH_CNT 0x000000FA +#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL /* * Increments PMC1 checking if MMCR1_PMC1SEL has one of the following * events: @@ -211,6 +212,92 @@ static void pmu_inc_pmc5(DisasContext *ctx) tcg_temp_free(t0); tcg_temp_free(t1); } + +/* + * Check for overflow of PMC1-PMC5 counters and call the overflow + * helper in case any counter has overflown. + */ +static void pmu_check_overflow(DisasContext *ctx) +{ + TCGv t_pmc1, t_pmc2, t_pmc3, t_pmc4, t_pmc5; + TCGv t0, t1; + TCGLabel *l_pmc_overflow; + TCGLabel *l_skip_pmc1_overflow; + TCGLabel *l_skip_overflow; + + /* + * Check if we have overflow bits set and fire an overflow + * event if necessary. Skip directly to 'l_pmc_overflow' + * right after finding the first overflow. + */ + l_pmc_overflow = gen_new_label(); + l_skip_pmc1_overflow = gen_new_label(); + + t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_POWER_MMCR0); + tcg_gen_andi_tl(t0, t0, MMCR0_PMC1CE); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, MMCR0_PMC1CE, l_skip_pmc1_overflow); + + t_pmc1 = tcg_temp_new(); + gen_load_spr(t_pmc1, SPR_POWER_PMC1); + tcg_gen_brcondi_tl(TCG_COND_GE, t_pmc1, PMC_COUNTER_NEGATIVE_VAL, + l_pmc_overflow); + + gen_set_label(l_skip_pmc1_overflow); + + l_skip_overflow = gen_new_label(); + + /* + * At this point we're sure PMC1 didn't overflow. If MMCR0_PMCjCE + * isn't set we can skip everything since PMC2-5 overflow is + * disabled. + */ + t1 = tcg_temp_new(); + gen_load_spr(t1, SPR_POWER_MMCR0); + tcg_gen_andi_tl(t1, t1, MMCR0_PMCjCE); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, MMCR0_PMCjCE, l_skip_overflow); + + t_pmc2 = tcg_temp_new(); + gen_load_spr(t_pmc2, SPR_POWER_PMC2); + tcg_gen_brcondi_tl(TCG_COND_GE, t_pmc2, PMC_COUNTER_NEGATIVE_VAL, + l_pmc_overflow); + + t_pmc3 = tcg_temp_new(); + gen_load_spr(t_pmc3, SPR_POWER_PMC3); + tcg_gen_brcondi_tl(TCG_COND_GE, t_pmc3, PMC_COUNTER_NEGATIVE_VAL, + l_pmc_overflow); + + t_pmc4 = tcg_temp_new(); + gen_load_spr(t_pmc4, SPR_POWER_PMC4); + tcg_gen_brcondi_tl(TCG_COND_GE, t_pmc4, PMC_COUNTER_NEGATIVE_VAL, + l_pmc_overflow); + + t_pmc5 = tcg_temp_new(); + gen_load_spr(t_pmc5, SPR_POWER_PMC5); + tcg_gen_brcondi_tl(TCG_COND_LE, t_pmc5, PMC_COUNTER_NEGATIVE_VAL, + l_skip_overflow); + + gen_set_label(l_pmc_overflow); + + /* + * The PMU overflow helper manipuilates the internal PMU timer. + * In that case, if the guest is running with icount and we do not + * handle it beforehand, the helper can trigger a 'bad icount + * read'. + */ + gen_icount_io_start(ctx); + gen_helper_pmu_overflow(cpu_env); + + gen_set_label(l_skip_overflow); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t_pmc1); + tcg_temp_free(t_pmc2); + tcg_temp_free(t_pmc3); + tcg_temp_free(t_pmc4); + tcg_temp_free(t_pmc5); +} #endif /* #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ #if defined(TARGET_PPC64) @@ -254,6 +341,8 @@ static void pmu_count_insns(DisasContext *ctx) gen_set_label(l_skip_pmc14); + pmu_check_overflow(ctx); + tcg_temp_free(t_mmcr0); tcg_temp_free(t_mmcr1); diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index 08d1902cd5..6696c9b3ae 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -323,6 +323,14 @@ void helper_insns_inc(CPUPPCState *env, uint32_t num_insns) } } +/* Helper to fire a PMC interrupt from TCG code */ +void helper_pmu_overflow(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + + fire_PMC_interrupt(cpu); +} + static void cpu_ppc_pmu_timer_cb(void *opaque) { PowerPCCPU *cpu = opaque; -- 2.33.1