Message ID | 20230530130447.372617-1-npiggin@gmail.com |
---|---|
State | New |
Headers | show |
Series | [v4] target/ppc: Fix PMU hflags calculation | expand |
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com> and queued. Thanks, Daniel On 5/30/23 10:04, Nicholas Piggin wrote: > Some of the PMU hflags bits can go out of synch, for example a store to > MMCR0 with PMCjCE=1 fails to update hflags correctly and results in > hflags mismatch: > > qemu: fatal: TCG hflags mismatch (current:0x2408003d rebuilt:0x240a003d) > > This can be reproduced by running perf on a recent machine. > > Some of the fragility here is the duplication of PMU hflags calculations. > This change consolidates that in a single place to update pmu-related > hflags, to be called after a well defined state changes. > > The post-load PMU update is pulled out of the MSR update because it does > not depend on the MSR value. > > Fixes: 8b3d1c49a9f0 ("target/ppc: Add new PMC HFLAGS") > Signed-off-by: Nicholas Piggin <npiggin@gmail.com> > --- > This is a significant rework from v3, which missed a couple of hflags. > I think it's more robust. > > Question came up whether we should rearm overflow timers in something > like cpu post load, but that's for a later patch. > > This is probably a stable candidate but I will wait for upstream > before ccing. > > Thanks, > Nick > --- > > target/ppc/cpu_init.c | 2 +- > target/ppc/helper_regs.c | 73 ++++++++++++++++++++++++++++++---------- > target/ppc/helper_regs.h | 1 + > target/ppc/machine.c | 8 ++--- > target/ppc/power8-pmu.c | 38 ++++++++++++--------- > target/ppc/power8-pmu.h | 4 +-- > 6 files changed, 85 insertions(+), 41 deletions(-) > > diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c > index 05bf73296b..398f2d9966 100644 > --- a/target/ppc/cpu_init.c > +++ b/target/ppc/cpu_init.c > @@ -7083,7 +7083,7 @@ static void ppc_cpu_reset_hold(Object *obj) > if (env->mmu_model != POWERPC_MMU_REAL) { > ppc_tlb_invalidate_all(env); > } > - pmu_update_summaries(env); > + pmu_mmcr01_updated(env); > } > > /* clean any pending stop state */ > diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c > index fb351c303f..bc7e9d7eda 100644 > --- a/target/ppc/helper_regs.c > +++ b/target/ppc/helper_regs.c > @@ -47,6 +47,48 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env) > env->tgpr[3] = tmp; > } > > +static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) > +{ > + uint32_t hflags = 0; > + > +#if defined(TARGET_PPC64) > + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { > + hflags |= 1 << HFLAGS_PMCC0; > + } > + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { > + hflags |= 1 << HFLAGS_PMCC1; > + } > + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { > + hflags |= 1 << HFLAGS_PMCJCE; > + } > + > +#ifndef CONFIG_USER_ONLY > + if (env->pmc_ins_cnt) { > + hflags |= 1 << HFLAGS_INSN_CNT; > + } > + if (env->pmc_ins_cnt & 0x1e) { > + hflags |= 1 << HFLAGS_PMC_OTHER; > + } > +#endif > +#endif > + > + return hflags; > +} > + > +/* Mask of all PMU hflags */ > +static uint32_t hreg_compute_pmu_hflags_mask(CPUPPCState *env) > +{ > + uint32_t hflags_mask = 0; > +#if defined(TARGET_PPC64) > + hflags_mask |= 1 << HFLAGS_PMCC0; > + hflags_mask |= 1 << HFLAGS_PMCC1; > + hflags_mask |= 1 << HFLAGS_PMCJCE; > + hflags_mask |= 1 << HFLAGS_INSN_CNT; > + hflags_mask |= 1 << HFLAGS_PMC_OTHER; > +#endif > + return hflags_mask; > +} > + > static uint32_t hreg_compute_hflags_value(CPUPPCState *env) > { > target_ulong msr = env->msr; > @@ -104,30 +146,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) > if (env->spr[SPR_LPCR] & LPCR_HR) { > hflags |= 1 << HFLAGS_HR; > } > - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { > - hflags |= 1 << HFLAGS_PMCC0; > - } > - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { > - hflags |= 1 << HFLAGS_PMCC1; > - } > - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { > - hflags |= 1 << HFLAGS_PMCJCE; > - } > > #ifndef CONFIG_USER_ONLY > if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { > hflags |= 1 << HFLAGS_HV; > } > > -#if defined(TARGET_PPC64) > - if (env->pmc_ins_cnt) { > - hflags |= 1 << HFLAGS_INSN_CNT; > - } > - if (env->pmc_ins_cnt & 0x1e) { > - hflags |= 1 << HFLAGS_PMC_OTHER; > - } > -#endif > - > /* > * This is our encoding for server processors. The architecture > * specifies that there is no such thing as userspace with > @@ -172,6 +196,8 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) > hflags |= dmmu_idx << HFLAGS_DMMU_IDX; > #endif > > + hflags |= hreg_compute_pmu_hflags_value(env); > + > return hflags | (msr & msr_mask); > } > > @@ -180,6 +206,17 @@ void hreg_compute_hflags(CPUPPCState *env) > env->hflags = hreg_compute_hflags_value(env); > } > > +/* > + * This can be used as a lighter-weight alternative to hreg_compute_hflags > + * when PMU MMCR0 or pmc_ins_cnt changes. pmc_ins_cnt is changed by > + * pmu_update_summaries. > + */ > +void hreg_update_pmu_hflags(CPUPPCState *env) > +{ > + env->hflags &= ~hreg_compute_pmu_hflags_mask(env); > + env->hflags |= hreg_compute_pmu_hflags_value(env); > +} > + > #ifdef CONFIG_DEBUG_TCG > void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, > target_ulong *cs_base, uint32_t *flags) > diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h > index 42f26870b9..8196c1346d 100644 > --- a/target/ppc/helper_regs.h > +++ b/target/ppc/helper_regs.h > @@ -22,6 +22,7 @@ > > void hreg_swap_gpr_tgpr(CPUPPCState *env); > void hreg_compute_hflags(CPUPPCState *env); > +void hreg_update_pmu_hflags(CPUPPCState *env); > void cpu_interrupt_exittb(CPUState *cs); > int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv); > > diff --git a/target/ppc/machine.c b/target/ppc/machine.c > index be6eb3d968..134b16c625 100644 > --- a/target/ppc/machine.c > +++ b/target/ppc/machine.c > @@ -21,10 +21,6 @@ static void post_load_update_msr(CPUPPCState *env) > */ > env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB); > ppc_store_msr(env, msr); > - > - if (tcg_enabled()) { > - pmu_update_summaries(env); > - } > } > > static int get_avr(QEMUFile *f, void *pv, size_t size, > @@ -317,6 +313,10 @@ static int cpu_post_load(void *opaque, int version_id) > > post_load_update_msr(env); > > + if (tcg_enabled()) { > + pmu_mmcr01_updated(env); > + } > + > return 0; > } > > diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c > index 64a64865d7..c4c331c6b5 100644 > --- a/target/ppc/power8-pmu.c > +++ b/target/ppc/power8-pmu.c > @@ -31,7 +31,11 @@ static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn) > return env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE; > } > > -void pmu_update_summaries(CPUPPCState *env) > +/* > + * Called after MMCR0 or MMCR1 changes to update pmc_ins_cnt and pmc_cyc_cnt. > + * hflags must subsequently be updated. > + */ > +static void pmu_update_summaries(CPUPPCState *env) > { > target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0]; > target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1]; > @@ -39,7 +43,7 @@ void pmu_update_summaries(CPUPPCState *env) > int cyc_cnt = 0; > > if (mmcr0 & MMCR0_FC) { > - goto hflags_calc; > + goto out; > } > > if (!(mmcr0 & MMCR0_FC14) && mmcr1 != 0) { > @@ -73,10 +77,19 @@ void pmu_update_summaries(CPUPPCState *env) > ins_cnt |= !(mmcr0 & MMCR0_FC56) << 5; > cyc_cnt |= !(mmcr0 & MMCR0_FC56) << 6; > > - hflags_calc: > + out: > env->pmc_ins_cnt = ins_cnt; > env->pmc_cyc_cnt = cyc_cnt; > - env->hflags = deposit32(env->hflags, HFLAGS_INSN_CNT, 1, ins_cnt != 0); > +} > + > +void pmu_mmcr01_updated(CPUPPCState *env) > +{ > + pmu_update_summaries(env); > + hreg_update_pmu_hflags(env); > + /* > + * Should this update overflow timers (if mmcr0 is updated) so they > + * get set in cpu_post_load? > + */ > } > > static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns) > @@ -234,18 +247,11 @@ static void pmu_delete_timers(CPUPPCState *env) > > void helper_store_mmcr0(CPUPPCState *env, target_ulong value) > { > - bool hflags_pmcc0 = (value & MMCR0_PMCC0) != 0; > - bool hflags_pmcc1 = (value & MMCR0_PMCC1) != 0; > - > pmu_update_cycles(env); > > env->spr[SPR_POWER_MMCR0] = value; > > - /* MMCR0 writes can change HFLAGS_PMCC[01] and HFLAGS_INSN_CNT */ > - env->hflags = deposit32(env->hflags, HFLAGS_PMCC0, 1, hflags_pmcc0); > - env->hflags = deposit32(env->hflags, HFLAGS_PMCC1, 1, hflags_pmcc1); > - > - pmu_update_summaries(env); > + pmu_mmcr01_updated(env); > > /* Update cycle overflow timers with the current MMCR0 state */ > pmu_update_overflow_timers(env); > @@ -257,8 +263,7 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value) > > env->spr[SPR_POWER_MMCR1] = value; > > - /* MMCR1 writes can change HFLAGS_INSN_CNT */ > - pmu_update_summaries(env); > + pmu_mmcr01_updated(env); > } > > target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn) > @@ -287,8 +292,8 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) > env->spr[SPR_POWER_MMCR0] &= ~MMCR0_FCECE; > env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; > > - /* Changing MMCR0_FC requires a new HFLAGS_INSN_CNT calc */ > - pmu_update_summaries(env); > + /* Changing MMCR0_FC requires summaries and hflags update */ > + pmu_mmcr01_updated(env); > > /* > * Delete all pending timers if we need to freeze > @@ -299,6 +304,7 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) > } > > if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE) { > + /* These MMCR0 bits do not require summaries or hflags update. */ > env->spr[SPR_POWER_MMCR0] &= ~MMCR0_PMAE; > env->spr[SPR_POWER_MMCR0] |= MMCR0_PMAO; > } > diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h > index c0093e2219..775e640053 100644 > --- a/target/ppc/power8-pmu.h > +++ b/target/ppc/power8-pmu.h > @@ -18,10 +18,10 @@ > #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL > > void cpu_ppc_pmu_init(CPUPPCState *env); > -void pmu_update_summaries(CPUPPCState *env); > +void pmu_mmcr01_updated(CPUPPCState *env); > #else > static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } > -static inline void pmu_update_summaries(CPUPPCState *env) { } > +static inline void pmu_mmcr01_updated(CPUPPCState *env) { } > #endif > > #endif
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 05bf73296b..398f2d9966 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7083,7 +7083,7 @@ static void ppc_cpu_reset_hold(Object *obj) if (env->mmu_model != POWERPC_MMU_REAL) { ppc_tlb_invalidate_all(env); } - pmu_update_summaries(env); + pmu_mmcr01_updated(env); } /* clean any pending stop state */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index fb351c303f..bc7e9d7eda 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -47,6 +47,48 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env) env->tgpr[3] = tmp; } +static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) +{ + uint32_t hflags = 0; + +#if defined(TARGET_PPC64) + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { + hflags |= 1 << HFLAGS_PMCC0; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { + hflags |= 1 << HFLAGS_PMCC1; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { + hflags |= 1 << HFLAGS_PMCJCE; + } + +#ifndef CONFIG_USER_ONLY + if (env->pmc_ins_cnt) { + hflags |= 1 << HFLAGS_INSN_CNT; + } + if (env->pmc_ins_cnt & 0x1e) { + hflags |= 1 << HFLAGS_PMC_OTHER; + } +#endif +#endif + + return hflags; +} + +/* Mask of all PMU hflags */ +static uint32_t hreg_compute_pmu_hflags_mask(CPUPPCState *env) +{ + uint32_t hflags_mask = 0; +#if defined(TARGET_PPC64) + hflags_mask |= 1 << HFLAGS_PMCC0; + hflags_mask |= 1 << HFLAGS_PMCC1; + hflags_mask |= 1 << HFLAGS_PMCJCE; + hflags_mask |= 1 << HFLAGS_INSN_CNT; + hflags_mask |= 1 << HFLAGS_PMC_OTHER; +#endif + return hflags_mask; +} + static uint32_t hreg_compute_hflags_value(CPUPPCState *env) { target_ulong msr = env->msr; @@ -104,30 +146,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_HR) { hflags |= 1 << HFLAGS_HR; } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { - hflags |= 1 << HFLAGS_PMCC0; - } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { - hflags |= 1 << HFLAGS_PMCC1; - } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { - hflags |= 1 << HFLAGS_PMCJCE; - } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { hflags |= 1 << HFLAGS_HV; } -#if defined(TARGET_PPC64) - if (env->pmc_ins_cnt) { - hflags |= 1 << HFLAGS_INSN_CNT; - } - if (env->pmc_ins_cnt & 0x1e) { - hflags |= 1 << HFLAGS_PMC_OTHER; - } -#endif - /* * This is our encoding for server processors. The architecture * specifies that there is no such thing as userspace with @@ -172,6 +196,8 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) hflags |= dmmu_idx << HFLAGS_DMMU_IDX; #endif + hflags |= hreg_compute_pmu_hflags_value(env); + return hflags | (msr & msr_mask); } @@ -180,6 +206,17 @@ void hreg_compute_hflags(CPUPPCState *env) env->hflags = hreg_compute_hflags_value(env); } +/* + * This can be used as a lighter-weight alternative to hreg_compute_hflags + * when PMU MMCR0 or pmc_ins_cnt changes. pmc_ins_cnt is changed by + * pmu_update_summaries. + */ +void hreg_update_pmu_hflags(CPUPPCState *env) +{ + env->hflags &= ~hreg_compute_pmu_hflags_mask(env); + env->hflags |= hreg_compute_pmu_hflags_value(env); +} + #ifdef CONFIG_DEBUG_TCG void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 42f26870b9..8196c1346d 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -22,6 +22,7 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env); void hreg_compute_hflags(CPUPPCState *env); +void hreg_update_pmu_hflags(CPUPPCState *env); void cpu_interrupt_exittb(CPUState *cs); int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv); diff --git a/target/ppc/machine.c b/target/ppc/machine.c index be6eb3d968..134b16c625 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -21,10 +21,6 @@ static void post_load_update_msr(CPUPPCState *env) */ env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB); ppc_store_msr(env, msr); - - if (tcg_enabled()) { - pmu_update_summaries(env); - } } static int get_avr(QEMUFile *f, void *pv, size_t size, @@ -317,6 +313,10 @@ static int cpu_post_load(void *opaque, int version_id) post_load_update_msr(env); + if (tcg_enabled()) { + pmu_mmcr01_updated(env); + } + return 0; } diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index 64a64865d7..c4c331c6b5 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -31,7 +31,11 @@ static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn) return env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE; } -void pmu_update_summaries(CPUPPCState *env) +/* + * Called after MMCR0 or MMCR1 changes to update pmc_ins_cnt and pmc_cyc_cnt. + * hflags must subsequently be updated. + */ +static void pmu_update_summaries(CPUPPCState *env) { target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0]; target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1]; @@ -39,7 +43,7 @@ void pmu_update_summaries(CPUPPCState *env) int cyc_cnt = 0; if (mmcr0 & MMCR0_FC) { - goto hflags_calc; + goto out; } if (!(mmcr0 & MMCR0_FC14) && mmcr1 != 0) { @@ -73,10 +77,19 @@ void pmu_update_summaries(CPUPPCState *env) ins_cnt |= !(mmcr0 & MMCR0_FC56) << 5; cyc_cnt |= !(mmcr0 & MMCR0_FC56) << 6; - hflags_calc: + out: env->pmc_ins_cnt = ins_cnt; env->pmc_cyc_cnt = cyc_cnt; - env->hflags = deposit32(env->hflags, HFLAGS_INSN_CNT, 1, ins_cnt != 0); +} + +void pmu_mmcr01_updated(CPUPPCState *env) +{ + pmu_update_summaries(env); + hreg_update_pmu_hflags(env); + /* + * Should this update overflow timers (if mmcr0 is updated) so they + * get set in cpu_post_load? + */ } static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns) @@ -234,18 +247,11 @@ static void pmu_delete_timers(CPUPPCState *env) void helper_store_mmcr0(CPUPPCState *env, target_ulong value) { - bool hflags_pmcc0 = (value & MMCR0_PMCC0) != 0; - bool hflags_pmcc1 = (value & MMCR0_PMCC1) != 0; - pmu_update_cycles(env); env->spr[SPR_POWER_MMCR0] = value; - /* MMCR0 writes can change HFLAGS_PMCC[01] and HFLAGS_INSN_CNT */ - env->hflags = deposit32(env->hflags, HFLAGS_PMCC0, 1, hflags_pmcc0); - env->hflags = deposit32(env->hflags, HFLAGS_PMCC1, 1, hflags_pmcc1); - - pmu_update_summaries(env); + pmu_mmcr01_updated(env); /* Update cycle overflow timers with the current MMCR0 state */ pmu_update_overflow_timers(env); @@ -257,8 +263,7 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value) env->spr[SPR_POWER_MMCR1] = value; - /* MMCR1 writes can change HFLAGS_INSN_CNT */ - pmu_update_summaries(env); + pmu_mmcr01_updated(env); } target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn) @@ -287,8 +292,8 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) env->spr[SPR_POWER_MMCR0] &= ~MMCR0_FCECE; env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; - /* Changing MMCR0_FC requires a new HFLAGS_INSN_CNT calc */ - pmu_update_summaries(env); + /* Changing MMCR0_FC requires summaries and hflags update */ + pmu_mmcr01_updated(env); /* * Delete all pending timers if we need to freeze @@ -299,6 +304,7 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) } if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE) { + /* These MMCR0 bits do not require summaries or hflags update. */ env->spr[SPR_POWER_MMCR0] &= ~MMCR0_PMAE; env->spr[SPR_POWER_MMCR0] |= MMCR0_PMAO; } diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index c0093e2219..775e640053 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -18,10 +18,10 @@ #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL void cpu_ppc_pmu_init(CPUPPCState *env); -void pmu_update_summaries(CPUPPCState *env); +void pmu_mmcr01_updated(CPUPPCState *env); #else static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } -static inline void pmu_update_summaries(CPUPPCState *env) { } +static inline void pmu_mmcr01_updated(CPUPPCState *env) { } #endif #endif
Some of the PMU hflags bits can go out of synch, for example a store to MMCR0 with PMCjCE=1 fails to update hflags correctly and results in hflags mismatch: qemu: fatal: TCG hflags mismatch (current:0x2408003d rebuilt:0x240a003d) This can be reproduced by running perf on a recent machine. Some of the fragility here is the duplication of PMU hflags calculations. This change consolidates that in a single place to update pmu-related hflags, to be called after a well defined state changes. The post-load PMU update is pulled out of the MSR update because it does not depend on the MSR value. Fixes: 8b3d1c49a9f0 ("target/ppc: Add new PMC HFLAGS") Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- This is a significant rework from v3, which missed a couple of hflags. I think it's more robust. Question came up whether we should rearm overflow timers in something like cpu post load, but that's for a later patch. This is probably a stable candidate but I will wait for upstream before ccing. Thanks, Nick --- target/ppc/cpu_init.c | 2 +- target/ppc/helper_regs.c | 73 ++++++++++++++++++++++++++++++---------- target/ppc/helper_regs.h | 1 + target/ppc/machine.c | 8 ++--- target/ppc/power8-pmu.c | 38 ++++++++++++--------- target/ppc/power8-pmu.h | 4 +-- 6 files changed, 85 insertions(+), 41 deletions(-)