+static bool pmc_is_active(CPUPPCState *env, int sprn)
+{
+ if (sprn < SPR_POWER_PMC5) {
+ return !(env->spr[SPR_POWER_MMCR0] & MMCR0_FC14);
+ }
+
+ return !(env->spr[SPR_POWER_MMCR0] & MMCR0_FC56);
+}
+
+static void pmu_update_cycles(CPUPPCState *env)
+{
+ uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ uint64_t time_delta = now - env->pmu_base_time;
+ int sprn;
+
+ for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
+ if (!pmc_is_active(env, sprn) ||
+ getPMUEventType(env, sprn) != PMU_EVENT_CYCLES) {
+ continue;
+ }
+
+ /*
+ * The pseries and powernv clock runs at 1Ghz, meaning
+ * that 1 nanosec equals 1 cycle.
+ */
+ env->spr[sprn] += time_delta;
+ }
+
+ /*
+ * Update base_time for future calculations if we updated
+ * the PMCs while the PMU was running.
+ */
+ if (!(env->spr[SPR_POWER_MMCR0] & MMCR0_FC)) {
+ env->pmu_base_time = now;
+ }
+}
+
+/*
+ * A cycle count session consists of the basic operations we
+ * need to do to support PM_CYC events: redefine a new base_time
+ * to be used to calculate PMC values and start overflow timers.
+ */
+static void start_cycle_count_session(CPUPPCState *env)
+{
+ /* Just define pmu_base_time for now */
+ env->pmu_base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+void helper_store_mmcr0(CPUPPCState *env, target_ulong value)
+{
+ target_ulong curr_value = env->spr[SPR_POWER_MMCR0];
+ bool curr_FC = curr_value & MMCR0_FC;
+ bool new_FC = value & MMCR0_FC;
+
+ env->spr[SPR_POWER_MMCR0] = value;
+
+ /* MMCR0 writes can change HFLAGS_PMCCCLEAR and HFLAGS_MMCR0FC */
+ if (((curr_value & MMCR0_PMCC) != (value & MMCR0_PMCC)) ||
+ (curr_FC != new_FC)) {
+ hreg_compute_hflags(env);
+ }
+
+ /*
+ * In an frozen count (FC) bit change:
+ *
+ * - if PMCs were running (curr_FC = false) and we're freezing
+ * them (new_FC = true), save the PMCs values in the registers.
+ *
+ * - if PMCs were frozen (curr_FC = true) and we're activating
+ * them (new_FC = false), set the new base_time for future cycle
+ * calculations.
+ */
+ if (curr_FC != new_FC) {
+ if (!curr_FC) { > + pmu_update_cycles(env);
+ } else {
+ start_cycle_count_session(env);
+ }
+ }
+}